From 3c3d49899f0f7206e190bdeecdc4da248cc7e686 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 8 Apr 2022 12:22:10 +0800 Subject: [PATCH] Remove dependent on session auth for api/v1 routers (#19321) * Remove dependent on session auth for api/v1 routers * Remove unnecessary session on API context * remove missed header * fix test * fix missed api/v1 --- integrations/api_issue_test.go | 61 ++++++++------- integrations/api_org_test.go | 17 ++--- integrations/api_releases_test.go | 11 +-- integrations/api_repo_topic_test.go | 42 +++++------ integrations/api_team_test.go | 17 ++--- integrations/api_user_heatmap_test.go | 6 +- integrations/integration_test.go | 4 + integrations/issue_test.go | 22 +++--- modules/context/api.go | 25 +------ routers/api/v1/api.go | 10 +-- routers/init.go | 18 +---- routers/web/misc/markdown.go | 98 +++++++++++++++++++++++++ routers/{api/v1 => web}/misc/swagger.go | 0 routers/web/web.go | 19 ++++- services/auth/auth.go | 10 --- templates/repo/diff/box.tmpl | 2 +- templates/repo/diff/comment_form.tmpl | 2 +- templates/repo/editor/edit.tmpl | 4 +- templates/repo/issue/comment_tab.tmpl | 4 +- templates/repo/issue/view_content.tmpl | 2 +- templates/repo/release/new.tmpl | 2 +- templates/repo/wiki/new.tmpl | 4 +- 22 files changed, 219 insertions(+), 161 deletions(-) create mode 100644 routers/web/misc/markdown.go rename routers/{api/v1 => web}/misc/swagger.go (100%) diff --git a/integrations/api_issue_test.go b/integrations/api_issue_test.go index 3957c1023..5ed5a0ad9 100644 --- a/integrations/api_issue_test.go +++ b/integrations/api_issue_test.go @@ -168,12 +168,11 @@ func TestAPIEditIssue(t *testing.T) { func TestAPISearchIssues(t *testing.T) { defer prepareTestEnv(t)() - session := loginUser(t, "user2") - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, "user2") link, _ := url.Parse("/api/v1/repos/issues/search") - req := NewRequest(t, "GET", link.String()) - resp := session.MakeRequest(t, req, http.StatusOK) + req := NewRequest(t, "GET", link.String()+"?token="+token) + resp := MakeRequest(t, req, http.StatusOK) var apiIssues []*api.Issue DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 10) @@ -181,7 +180,7 @@ func TestAPISearchIssues(t *testing.T) { query := url.Values{"token": {token}} link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 10) @@ -189,9 +188,10 @@ func TestAPISearchIssues(t *testing.T) { before := time.Unix(999307200, 0).Format(time.RFC3339) query.Add("since", since) query.Add("before", before) + query.Add("token", token) link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 8) query.Del("since") @@ -200,14 +200,14 @@ func TestAPISearchIssues(t *testing.T) { query.Add("state", "closed") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 2) query.Set("state", "all") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.EqualValues(t, "15", resp.Header().Get("X-Total-Count")) assert.Len(t, apiIssues, 10) // there are more but 10 is page item limit @@ -215,49 +215,49 @@ func TestAPISearchIssues(t *testing.T) { query.Add("limit", "20") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 15) - query = url.Values{"assigned": {"true"}, "state": {"all"}} + query = url.Values{"assigned": {"true"}, "state": {"all"}, "token": {token}} link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 1) - query = url.Values{"milestones": {"milestone1"}, "state": {"all"}} + query = url.Values{"milestones": {"milestone1"}, "state": {"all"}, "token": {token}} link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 1) - query = url.Values{"milestones": {"milestone1,milestone3"}, "state": {"all"}} + query = url.Values{"milestones": {"milestone1,milestone3"}, "state": {"all"}, "token": {token}} link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 2) - query = url.Values{"owner": {"user2"}} // user + query = url.Values{"owner": {"user2"}, "token": {token}} // user link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 6) - query = url.Values{"owner": {"user3"}} // organization + query = url.Values{"owner": {"user3"}, "token": {token}} // organization link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 3) - query = url.Values{"owner": {"user3"}, "team": {"team1"}} // organization + team + query = url.Values{"owner": {"user3"}, "team": {"team1"}, "token": {token}} // organization + team link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 2) } @@ -265,12 +265,11 @@ func TestAPISearchIssues(t *testing.T) { func TestAPISearchIssuesWithLabels(t *testing.T) { defer prepareTestEnv(t)() - session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, "user1") link, _ := url.Parse("/api/v1/repos/issues/search") - req := NewRequest(t, "GET", link.String()) - resp := session.MakeRequest(t, req, http.StatusOK) + req := NewRequest(t, "GET", link.String()+"?token="+token) + resp := MakeRequest(t, req, http.StatusOK) var apiIssues []*api.Issue DecodeJSON(t, resp, &apiIssues) @@ -280,14 +279,14 @@ func TestAPISearchIssuesWithLabels(t *testing.T) { query.Add("token", token) link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 10) query.Add("labels", "label1") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 2) @@ -295,7 +294,7 @@ func TestAPISearchIssuesWithLabels(t *testing.T) { query.Set("labels", "label1,label2") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 2) @@ -303,7 +302,7 @@ func TestAPISearchIssuesWithLabels(t *testing.T) { query.Set("labels", "orglabel4") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 1) @@ -312,7 +311,7 @@ func TestAPISearchIssuesWithLabels(t *testing.T) { query.Add("state", "all") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 2) @@ -320,7 +319,7 @@ func TestAPISearchIssuesWithLabels(t *testing.T) { query.Set("labels", "label1,orglabel4") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 2) } diff --git a/integrations/api_org_test.go b/integrations/api_org_test.go index e33c010e8..a3c1827e7 100644 --- a/integrations/api_org_test.go +++ b/integrations/api_org_test.go @@ -20,9 +20,8 @@ import ( func TestAPIOrgCreate(t *testing.T) { onGiteaRun(t, func(*testing.T, *url.URL) { - session := loginUser(t, "user1") + token := getUserToken(t, "user1") - token := getTokenForLoggedInUser(t, session) org := api.CreateOrgOption{ UserName: "user1_org", FullName: "User1's organization", @@ -32,7 +31,7 @@ func TestAPIOrgCreate(t *testing.T) { Visibility: "limited", } req := NewRequestWithJSON(t, "POST", "/api/v1/orgs?token="+token, &org) - resp := session.MakeRequest(t, req, http.StatusCreated) + resp := MakeRequest(t, req, http.StatusCreated) var apiOrg api.Organization DecodeJSON(t, resp, &apiOrg) @@ -50,13 +49,13 @@ func TestAPIOrgCreate(t *testing.T) { FullName: org.FullName, }) - req = NewRequestf(t, "GET", "/api/v1/orgs/%s", org.UserName) - resp = session.MakeRequest(t, req, http.StatusOK) + req = NewRequestf(t, "GET", "/api/v1/orgs/%s?token=%s", org.UserName, token) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiOrg) assert.EqualValues(t, org.UserName, apiOrg.UserName) - req = NewRequestf(t, "GET", "/api/v1/orgs/%s/repos", org.UserName) - resp = session.MakeRequest(t, req, http.StatusOK) + req = NewRequestf(t, "GET", "/api/v1/orgs/%s/repos?token=%s", org.UserName, token) + resp = MakeRequest(t, req, http.StatusOK) var repos []*api.Repository DecodeJSON(t, resp, &repos) @@ -64,8 +63,8 @@ func TestAPIOrgCreate(t *testing.T) { assert.False(t, repo.Private) } - req = NewRequestf(t, "GET", "/api/v1/orgs/%s/members", org.UserName) - resp = session.MakeRequest(t, req, http.StatusOK) + req = NewRequestf(t, "GET", "/api/v1/orgs/%s/members?token=%s", org.UserName, token) + resp = MakeRequest(t, req, http.StatusOK) // user1 on this org is public var users []*api.User diff --git a/integrations/api_releases_test.go b/integrations/api_releases_test.go index 80f53717b..ebb76cc16 100644 --- a/integrations/api_releases_test.go +++ b/integrations/api_releases_test.go @@ -25,12 +25,11 @@ func TestAPIListReleases(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) - session := loginUser(t, user2.LowerName) - token := getTokenForLoggedInUser(t, session) + token := getUserToken(t, user2.LowerName) link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/releases", user2.Name, repo.Name)) link.RawQuery = url.Values{"token": {token}}.Encode() - resp := session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) + resp := MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) var apiReleases []*api.Release DecodeJSON(t, resp, &apiReleases) if assert.Len(t, apiReleases, 3) { @@ -53,13 +52,11 @@ func TestAPIListReleases(t *testing.T) { // test filter testFilterByLen := func(auth bool, query url.Values, expectedLength int, msgAndArgs ...string) { - link.RawQuery = query.Encode() if auth { query.Set("token", token) - resp = session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) - } else { - resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) } + link.RawQuery = query.Encode() + resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) DecodeJSON(t, resp, &apiReleases) assert.Len(t, apiReleases, expectedLength, msgAndArgs) } diff --git a/integrations/api_repo_topic_test.go b/integrations/api_repo_topic_test.go index b7f9a5a5a..04295724a 100644 --- a/integrations/api_repo_topic_test.go +++ b/integrations/api_repo_topic_test.go @@ -59,36 +59,34 @@ func TestAPIRepoTopic(t *testing.T) { repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}).(*repo_model.Repository) // Get user2's token - session := loginUser(t, user2.Name) - token2 := getTokenForLoggedInUser(t, session) + token2 := getUserToken(t, user2.Name) // Test read topics using login url := fmt.Sprintf("/api/v1/repos/%s/%s/topics", user2.Name, repo2.Name) - req := NewRequest(t, "GET", url) - res := session.MakeRequest(t, req, http.StatusOK) + req := NewRequest(t, "GET", url+"?token="+token2) + res := MakeRequest(t, req, http.StatusOK) var topics *api.TopicName DecodeJSON(t, res, &topics) assert.ElementsMatch(t, []string{"topicname1", "topicname2"}, topics.TopicNames) // Log out user2 - session = emptyTestSession(t) url = fmt.Sprintf("/api/v1/repos/%s/%s/topics?token=%s", user2.Name, repo2.Name, token2) // Test delete a topic req = NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/topics/%s?token=%s", user2.Name, repo2.Name, "Topicname1", token2) - session.MakeRequest(t, req, http.StatusNoContent) + MakeRequest(t, req, http.StatusNoContent) // Test add an existing topic req = NewRequestf(t, "PUT", "/api/v1/repos/%s/%s/topics/%s?token=%s", user2.Name, repo2.Name, "Golang", token2) - session.MakeRequest(t, req, http.StatusNoContent) + MakeRequest(t, req, http.StatusNoContent) // Test add a topic req = NewRequestf(t, "PUT", "/api/v1/repos/%s/%s/topics/%s?token=%s", user2.Name, repo2.Name, "topicName3", token2) - session.MakeRequest(t, req, http.StatusNoContent) + MakeRequest(t, req, http.StatusNoContent) // Test read topics using token req = NewRequest(t, "GET", url) - res = session.MakeRequest(t, req, http.StatusOK) + res = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, res, &topics) assert.ElementsMatch(t, []string{"topicname2", "golang", "topicname3"}, topics.TopicNames) @@ -97,9 +95,9 @@ func TestAPIRepoTopic(t *testing.T) { req = NewRequestWithJSON(t, "PUT", url, &api.RepoTopicOptions{ Topics: newTopics, }) - session.MakeRequest(t, req, http.StatusNoContent) + MakeRequest(t, req, http.StatusNoContent) req = NewRequest(t, "GET", url) - res = session.MakeRequest(t, req, http.StatusOK) + res = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, res, &topics) assert.ElementsMatch(t, []string{"windows", "mac"}, topics.TopicNames) @@ -108,9 +106,9 @@ func TestAPIRepoTopic(t *testing.T) { req = NewRequestWithJSON(t, "PUT", url, &api.RepoTopicOptions{ Topics: newTopics, }) - session.MakeRequest(t, req, http.StatusUnprocessableEntity) + MakeRequest(t, req, http.StatusUnprocessableEntity) req = NewRequest(t, "GET", url) - res = session.MakeRequest(t, req, http.StatusOK) + res = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, res, &topics) assert.ElementsMatch(t, []string{"windows", "mac"}, topics.TopicNames) @@ -119,9 +117,9 @@ func TestAPIRepoTopic(t *testing.T) { req = NewRequestWithJSON(t, "PUT", url, &api.RepoTopicOptions{ Topics: newTopics, }) - session.MakeRequest(t, req, http.StatusNoContent) + MakeRequest(t, req, http.StatusNoContent) req = NewRequest(t, "GET", url) - res = session.MakeRequest(t, req, http.StatusOK) + res = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, res, &topics) assert.Len(t, topics.TopicNames, 25) @@ -130,29 +128,27 @@ func TestAPIRepoTopic(t *testing.T) { req = NewRequestWithJSON(t, "PUT", url, &api.RepoTopicOptions{ Topics: newTopics, }) - session.MakeRequest(t, req, http.StatusUnprocessableEntity) + MakeRequest(t, req, http.StatusUnprocessableEntity) // Test add a topic when there is already maximum req = NewRequestf(t, "PUT", "/api/v1/repos/%s/%s/topics/%s?token=%s", user2.Name, repo2.Name, "t26", token2) - session.MakeRequest(t, req, http.StatusUnprocessableEntity) + MakeRequest(t, req, http.StatusUnprocessableEntity) // Test delete a topic that repo doesn't have req = NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/topics/%s?token=%s", user2.Name, repo2.Name, "Topicname1", token2) - session.MakeRequest(t, req, http.StatusNotFound) + MakeRequest(t, req, http.StatusNotFound) // Get user4's token - session = loginUser(t, user4.Name) - token4 := getTokenForLoggedInUser(t, session) - session = emptyTestSession(t) + token4 := getUserToken(t, user4.Name) // Test read topics with write access url = fmt.Sprintf("/api/v1/repos/%s/%s/topics?token=%s", user3.Name, repo3.Name, token4) req = NewRequest(t, "GET", url) - res = session.MakeRequest(t, req, http.StatusOK) + res = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, res, &topics) assert.Empty(t, topics.TopicNames) // Test add a topic to repo with write access (requires repo admin access) req = NewRequestf(t, "PUT", "/api/v1/repos/%s/%s/topics/%s?token=%s", user3.Name, repo3.Name, "topicName", token4) - session.MakeRequest(t, req, http.StatusForbidden) + MakeRequest(t, req, http.StatusForbidden) } diff --git a/integrations/api_team_test.go b/integrations/api_team_test.go index 7f6d1b81d..daf1efa2b 100644 --- a/integrations/api_team_test.go +++ b/integrations/api_team_test.go @@ -224,11 +224,9 @@ func TestAPITeamSearch(t *testing.T) { var results TeamSearchResults - session := loginUser(t, user.Name) - csrf := GetCSRF(t, session, "/"+org.Name) - req := NewRequestf(t, "GET", "/api/v1/orgs/%s/teams/search?q=%s", org.Name, "_team") - req.Header.Add("X-Csrf-Token", csrf) - resp := session.MakeRequest(t, req, http.StatusOK) + token := getUserToken(t, user.Name) + req := NewRequestf(t, "GET", "/api/v1/orgs/%s/teams/search?q=%s&token=%s", org.Name, "_team", token) + resp := MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &results) assert.NotEmpty(t, results.Data) assert.Len(t, results.Data, 1) @@ -236,9 +234,8 @@ func TestAPITeamSearch(t *testing.T) { // no access if not organization member user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User) - session = loginUser(t, user5.Name) - csrf = GetCSRF(t, session, "/"+org.Name) - req = NewRequestf(t, "GET", "/api/v1/orgs/%s/teams/search?q=%s", org.Name, "team") - req.Header.Add("X-Csrf-Token", csrf) - session.MakeRequest(t, req, http.StatusForbidden) + token5 := getUserToken(t, user5.Name) + + req = NewRequestf(t, "GET", "/api/v1/orgs/%s/teams/search?q=%s&token=%s", org.Name, "team", token5) + MakeRequest(t, req, http.StatusForbidden) } diff --git a/integrations/api_user_heatmap_test.go b/integrations/api_user_heatmap_test.go index 69f4ff224..62e70d4c3 100644 --- a/integrations/api_user_heatmap_test.go +++ b/integrations/api_user_heatmap_test.go @@ -20,15 +20,15 @@ func TestUserHeatmap(t *testing.T) { defer prepareTestEnv(t)() adminUsername := "user1" normalUsername := "user2" - session := loginUser(t, adminUsername) + token := getUserToken(t, adminUsername) fakeNow := time.Date(2011, 10, 20, 0, 0, 0, 0, time.Local) timeutil.Set(fakeNow) defer timeutil.Unset() - urlStr := fmt.Sprintf("/api/v1/users/%s/heatmap", normalUsername) + urlStr := fmt.Sprintf("/api/v1/users/%s/heatmap?token=%s", normalUsername, token) req := NewRequest(t, "GET", urlStr) - resp := session.MakeRequest(t, req, http.StatusOK) + resp := MakeRequest(t, req, http.StatusOK) var heatmap []*models.UserHeatmapData DecodeJSON(t, resp, &heatmap) var dummyheatmap []*models.UserHeatmapData diff --git a/integrations/integration_test.go b/integrations/integration_test.go index 9e0445cae..e7ceb33e9 100644 --- a/integrations/integration_test.go +++ b/integrations/integration_test.go @@ -359,6 +359,10 @@ func emptyTestSession(t testing.TB) *TestSession { return &TestSession{jar: jar} } +func getUserToken(t testing.TB, userName string) string { + return getTokenForLoggedInUser(t, loginUser(t, userName)) +} + func loginUser(t testing.TB, userName string) *TestSession { t.Helper() if session, ok := loginSessionCache[userName]; ok { diff --git a/integrations/issue_test.go b/integrations/issue_test.go index 8a58f59ba..c6b801c9a 100644 --- a/integrations/issue_test.go +++ b/integrations/issue_test.go @@ -448,27 +448,29 @@ func TestSearchIssues(t *testing.T) { func TestSearchIssuesWithLabels(t *testing.T) { defer prepareTestEnv(t)() - session := loginUser(t, "user1") + token := getUserToken(t, "user1") - link, _ := url.Parse("/api/v1/repos/issues/search") + link, _ := url.Parse("/api/v1/repos/issues/search?token=" + token) req := NewRequest(t, "GET", link.String()) - resp := session.MakeRequest(t, req, http.StatusOK) + resp := MakeRequest(t, req, http.StatusOK) var apiIssues []*api.Issue DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 10) - query := url.Values{} + query := url.Values{ + "token": []string{token}, + } link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 10) query.Add("labels", "label1") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 2) @@ -476,7 +478,7 @@ func TestSearchIssuesWithLabels(t *testing.T) { query.Set("labels", "label1,label2") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 2) @@ -484,7 +486,7 @@ func TestSearchIssuesWithLabels(t *testing.T) { query.Set("labels", "orglabel4") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 1) @@ -493,7 +495,7 @@ func TestSearchIssuesWithLabels(t *testing.T) { query.Add("state", "all") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 2) @@ -501,7 +503,7 @@ func TestSearchIssuesWithLabels(t *testing.T) { query.Set("labels", "label1,orglabel4") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) - resp = session.MakeRequest(t, req, http.StatusOK) + resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 2) } diff --git a/modules/context/api.go b/modules/context/api.go index da08f990b..e5c2eeda0 100644 --- a/modules/context/api.go +++ b/modules/context/api.go @@ -8,7 +8,6 @@ package context import ( "context" "fmt" - "html" "net/http" "net/url" "strings" @@ -20,8 +19,6 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web/middleware" auth_service "code.gitea.io/gitea/services/auth" - - "gitea.com/go-chi/session" ) // APIContext is a specific context for API service @@ -191,17 +188,6 @@ 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 { - Validate(ctx.Context, ctx.csrf) - } else { - ctx.Context.Error(http.StatusUnauthorized, "Missing CSRF token.") - } -} - // CheckForOTP validates OTP func (ctx *APIContext) CheckForOTP() { if skip, ok := ctx.Data["SkipLocalTwoFA"]; ok && skip.(bool) { @@ -253,17 +239,14 @@ func APIAuth(authMethod auth_service.Method) func(*APIContext) { // APIContexter returns apicontext as middleware func APIContexter() func(http.Handler) http.Handler { - csrfOpts := getCsrfOpts() - return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { locale := middleware.Locale(w, req) ctx := APIContext{ Context: &Context{ - Resp: NewResponse(w), - Data: map[string]interface{}{}, - Locale: locale, - Session: session.GetSession(req), + Resp: NewResponse(w), + Data: map[string]interface{}{}, + Locale: locale, Repo: &Repository{ PullRequest: &PullRequest{}, }, @@ -273,7 +256,6 @@ func APIContexter() func(http.Handler) http.Handler { } ctx.Req = WithAPIContext(WithContext(req, ctx.Context), &ctx) - ctx.csrf = Csrfer(csrfOpts, ctx.Context) // If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid. if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") { @@ -285,7 +267,6 @@ func APIContexter() func(http.Handler) http.Handler { ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions) - ctx.Data["CsrfToken"] = html.EscapeString(ctx.csrf.GetToken()) ctx.Data["Context"] = &ctx next.ServeHTTP(ctx.Resp, ctx.Req) diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 2c2926389..a430eb453 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -216,7 +216,6 @@ func reqToken() func(ctx *context.APIContext) { return } if ctx.IsSigned { - ctx.RequireCSRF() return } ctx.Error(http.StatusUnauthorized, "reqToken", "token is required") @@ -584,8 +583,7 @@ func bind(obj interface{}) http.HandlerFunc { func buildAuthGroup() *auth.Group { group := auth.NewGroup( &auth.OAuth2{}, - &auth.Basic{}, // FIXME: this should be removed once we don't allow basic auth in API - auth.SharedSession, // FIXME: this should be removed once all UI don't reference API/v1, see https://github.com/go-gitea/gitea/pull/16052 + &auth.Basic{}, // FIXME: this should be removed once we don't allow basic auth in API ) if setting.Service.EnableReverseProxyAuth { group.Add(&auth.ReverseProxy{}) @@ -596,11 +594,9 @@ func buildAuthGroup() *auth.Group { } // Routes registers all v1 APIs routes to web application. -func Routes(sessioner func(http.Handler) http.Handler) *web.Route { +func Routes() *web.Route { m := web.NewRoute() - m.Use(sessioner) - m.Use(securityHeaders()) if setting.CORSConfig.Enabled { m.Use(cors.Handler(cors.Options{ @@ -609,7 +605,7 @@ func Routes(sessioner func(http.Handler) http.Handler) *web.Route { // setting.CORSConfig.AllowSubdomain // FIXME: the cors middleware needs allowSubdomain option AllowedMethods: setting.CORSConfig.Methods, AllowCredentials: setting.CORSConfig.AllowCredentials, - AllowedHeaders: []string{"Authorization", "X-CSRFToken", "X-Gitea-OTP"}, + AllowedHeaders: []string{"Authorization", "X-Gitea-OTP"}, MaxAge: int(setting.CORSConfig.MaxAge.Seconds()), })) } diff --git a/routers/init.go b/routers/init.go index 62a9e4002..88c393736 100644 --- a/routers/init.go +++ b/routers/init.go @@ -48,8 +48,6 @@ import ( "code.gitea.io/gitea/services/repository/archiver" "code.gitea.io/gitea/services/task" "code.gitea.io/gitea/services/webhook" - - "gitea.com/go-chi/session" ) func mustInit(fn func() error) { @@ -174,20 +172,8 @@ func NormalRoutes() *web.Route { r.Use(middle) } - sessioner := session.Sessioner(session.Options{ - Provider: setting.SessionConfig.Provider, - ProviderConfig: setting.SessionConfig.ProviderConfig, - CookieName: setting.SessionConfig.CookieName, - CookiePath: setting.SessionConfig.CookiePath, - Gclifetime: setting.SessionConfig.Gclifetime, - Maxlifetime: setting.SessionConfig.Maxlifetime, - Secure: setting.SessionConfig.Secure, - SameSite: setting.SessionConfig.SameSite, - Domain: setting.SessionConfig.Domain, - }) - - r.Mount("/", web_routers.Routes(sessioner)) - r.Mount("/api/v1", apiv1.Routes(sessioner)) + r.Mount("/", web_routers.Routes()) + r.Mount("/api/v1", apiv1.Routes()) r.Mount("/api/internal", private.Routes()) if setting.Packages.Enabled { r.Mount("/api/packages", packages_router.Routes()) diff --git a/routers/web/misc/markdown.go b/routers/web/misc/markdown.go new file mode 100644 index 000000000..b37aaf10f --- /dev/null +++ b/routers/web/misc/markdown.go @@ -0,0 +1,98 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package misc + +import ( + "net/http" + "strings" + + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/markup/markdown" + "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" + "mvdan.cc/xurls/v2" +) + +// Markdown render markdown document to HTML +func Markdown(ctx *context.Context) { + // swagger:operation POST /markdown miscellaneous renderMarkdown + // --- + // summary: Render a markdown document as HTML + // parameters: + // - name: body + // in: body + // schema: + // "$ref": "#/definitions/MarkdownOption" + // consumes: + // - application/json + // produces: + // - text/html + // responses: + // "200": + // "$ref": "#/responses/MarkdownRender" + // "422": + // "$ref": "#/responses/validationError" + + form := web.GetForm(ctx).(*api.MarkdownOption) + + if ctx.HasAPIError() { + ctx.Error(http.StatusUnprocessableEntity, "", ctx.GetErrMsg()) + return + } + + if len(form.Text) == 0 { + _, _ = ctx.Write([]byte("")) + return + } + + switch form.Mode { + case "comment": + fallthrough + case "gfm": + urlPrefix := form.Context + meta := map[string]string{} + if !strings.HasPrefix(setting.AppSubURL+"/", urlPrefix) { + // check if urlPrefix is already set to a URL + linkRegex, _ := xurls.StrictMatchingScheme("https?://") + m := linkRegex.FindStringIndex(urlPrefix) + if m == nil { + urlPrefix = util.URLJoin(setting.AppURL, form.Context) + } + } + if ctx.Repo != nil && ctx.Repo.Repository != nil { + // "gfm" = Github Flavored Markdown - set this to render as a document + if form.Mode == "gfm" { + meta = ctx.Repo.Repository.ComposeDocumentMetas() + } else { + meta = ctx.Repo.Repository.ComposeMetas() + } + } + if form.Mode == "gfm" { + meta["mode"] = "document" + } + + if err := markdown.Render(&markup.RenderContext{ + Ctx: ctx, + URLPrefix: urlPrefix, + Metas: meta, + IsWiki: form.Wiki, + }, strings.NewReader(form.Text), ctx.Resp); err != nil { + ctx.Error(http.StatusInternalServerError, err.Error()) + return + } + default: + if err := markdown.RenderRaw(&markup.RenderContext{ + Ctx: ctx, + URLPrefix: form.Context, + }, strings.NewReader(form.Text), ctx.Resp); err != nil { + ctx.Error(http.StatusInternalServerError, err.Error()) + return + } + } +} diff --git a/routers/api/v1/misc/swagger.go b/routers/web/misc/swagger.go similarity index 100% rename from routers/api/v1/misc/swagger.go rename to routers/web/misc/swagger.go diff --git a/routers/web/web.go b/routers/web/web.go index 9a2e96aee..190ab099e 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -25,13 +25,13 @@ import ( "code.gitea.io/gitea/modules/validation" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/modules/web/routing" - "code.gitea.io/gitea/routers/api/v1/misc" "code.gitea.io/gitea/routers/web/admin" "code.gitea.io/gitea/routers/web/auth" "code.gitea.io/gitea/routers/web/dev" "code.gitea.io/gitea/routers/web/events" "code.gitea.io/gitea/routers/web/explore" "code.gitea.io/gitea/routers/web/feed" + "code.gitea.io/gitea/routers/web/misc" "code.gitea.io/gitea/routers/web/org" "code.gitea.io/gitea/routers/web/repo" "code.gitea.io/gitea/routers/web/user" @@ -46,6 +46,7 @@ import ( _ "code.gitea.io/gitea/modules/session" // to registers all internal adapters "gitea.com/go-chi/captcha" + "gitea.com/go-chi/session" "github.com/NYTimes/gziphandler" "github.com/go-chi/chi/v5/middleware" "github.com/go-chi/cors" @@ -85,7 +86,7 @@ func buildAuthGroup() *auth_service.Group { group := auth_service.NewGroup( &auth_service.OAuth2{}, // FIXME: this should be removed and only applied in download and oauth realted routers &auth_service.Basic{}, // FIXME: this should be removed and only applied in download and git/lfs routers - auth_service.SharedSession, + &auth_service.Session{}, ) if setting.Service.EnableReverseProxyAuth { group.Add(&auth_service.ReverseProxy{}) @@ -96,7 +97,7 @@ func buildAuthGroup() *auth_service.Group { } // Routes returns all web routes -func Routes(sessioner func(http.Handler) http.Handler) *web.Route { +func Routes() *web.Route { routes := web.NewRoute() routes.Use(web.WrapWithPrefix(public.AssetsURLPathPrefix, public.AssetsHandlerFunc(&public.Options{ @@ -105,6 +106,17 @@ func Routes(sessioner func(http.Handler) http.Handler) *web.Route { CorsHandler: CorsHandler(), }), "AssetsHandler")) + sessioner := session.Sessioner(session.Options{ + Provider: setting.SessionConfig.Provider, + ProviderConfig: setting.SessionConfig.ProviderConfig, + CookieName: setting.SessionConfig.CookieName, + CookiePath: setting.SessionConfig.CookiePath, + Gclifetime: setting.SessionConfig.Gclifetime, + Maxlifetime: setting.SessionConfig.Maxlifetime, + Secure: setting.SessionConfig.Secure, + SameSite: setting.SessionConfig.SameSite, + Domain: setting.SessionConfig.Domain, + }) routes.Use(sessioner) routes.Use(Recovery()) @@ -878,6 +890,7 @@ func RegisterRoutes(m *web.Route) { m.Group("/comments/{id}", func() { m.Get("/attachments", repo.GetCommentAttachments) }) + m.Post("/markdown", bindIgnErr(structs.MarkdownOption{}), misc.Markdown) m.Group("/labels", func() { m.Post("/new", bindIgnErr(forms.CreateLabelForm{}), repo.NewLabel) m.Post("/edit", bindIgnErr(forms.CreateLabelForm{}), repo.UpdateLabel) diff --git a/services/auth/auth.go b/services/auth/auth.go index 15df47da3..3a5bb9d27 100644 --- a/services/auth/auth.go +++ b/services/auth/auth.go @@ -20,16 +20,6 @@ import ( "code.gitea.io/gitea/modules/web/middleware" ) -// The purpose of the following three function variables is to let the linter know that -// those functions are not dead code and are actually being used -var ( - _ = handleSignIn - - // SharedSession the session auth should only be used by web, but now both web and API/v1 - // will use it. We can remove this after Web removed dependent API/v1 - SharedSession = &Session{} -) - // Init should be called exactly once when the application starts to allow plugins // to allocate necessary resources func Init() { diff --git a/templates/repo/diff/box.tmpl b/templates/repo/diff/box.tmpl index f115a5f49..b7135de59 100644 --- a/templates/repo/diff/box.tmpl +++ b/templates/repo/diff/box.tmpl @@ -162,7 +162,7 @@
diff --git a/templates/repo/diff/comment_form.tmpl b/templates/repo/diff/comment_form.tmpl index cb7234b3b..7f7fb7e32 100644 --- a/templates/repo/diff/comment_form.tmpl +++ b/templates/repo/diff/comment_form.tmpl @@ -11,7 +11,7 @@
diff --git a/templates/repo/editor/edit.tmpl b/templates/repo/editor/edit.tmpl index 5e3351c86..8135348b9 100644 --- a/templates/repo/editor/edit.tmpl +++ b/templates/repo/editor/edit.tmpl @@ -31,13 +31,13 @@
diff --git a/templates/repo/issue/view_content.tmpl b/templates/repo/issue/view_content.tmpl index 820055c13..46a2a3969 100644 --- a/templates/repo/issue/view_content.tmpl +++ b/templates/repo/issue/view_content.tmpl @@ -195,7 +195,7 @@
diff --git a/templates/repo/release/new.tmpl b/templates/repo/release/new.tmpl index 9193389e6..5275bf021 100644 --- a/templates/repo/release/new.tmpl +++ b/templates/repo/release/new.tmpl @@ -48,7 +48,7 @@
diff --git a/templates/repo/wiki/new.tmpl b/templates/repo/wiki/new.tmpl index 5b8cdc816..1f9c3788a 100644 --- a/templates/repo/wiki/new.tmpl +++ b/templates/repo/wiki/new.tmpl @@ -21,11 +21,11 @@
- +