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
This commit is contained in:
		
					parent
					
						
							
								75f8534c3a
							
						
					
				
			
			
				commit
				
					
						3c3d49899f
					
				
			
		
					 22 changed files with 219 additions and 161 deletions
				
			
		| 
						 | 
				
			
			@ -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)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,8 +239,6 @@ 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)
 | 
			
		||||
| 
						 | 
				
			
			@ -263,7 +247,6 @@ func APIContexter() func(http.Handler) http.Handler {
 | 
			
		|||
					Resp:   NewResponse(w),
 | 
			
		||||
					Data:   map[string]interface{}{},
 | 
			
		||||
					Locale: locale,
 | 
			
		||||
					Session: session.GetSession(req),
 | 
			
		||||
					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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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")
 | 
			
		||||
| 
						 | 
				
			
			@ -585,7 +584,6 @@ 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
 | 
			
		||||
	)
 | 
			
		||||
	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()),
 | 
			
		||||
		}))
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										98
									
								
								routers/web/misc/markdown.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								routers/web/misc/markdown.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -162,7 +162,7 @@
 | 
			
		|||
				<div class="ui comment form">
 | 
			
		||||
					<div class="ui top attached tabular menu">
 | 
			
		||||
						<a class="active write item">{{$.i18n.Tr "write"}}</a>
 | 
			
		||||
						<a class="preview item" data-url="{{$.Repository.APIURL}}/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a>
 | 
			
		||||
						<a class="preview item" data-url="{{$.Repository.HTMLURL}}/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a>
 | 
			
		||||
					</div>
 | 
			
		||||
					<div class="ui bottom attached active write tab segment">
 | 
			
		||||
						<textarea class="review-textarea" tabindex="1" name="content"></textarea>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,7 @@
 | 
			
		|||
		<input type="hidden" name="diff_base_cid">
 | 
			
		||||
		<div class="ui top tabular menu" data-write="write" data-preview="preview">
 | 
			
		||||
			<a class="active item" data-tab="write">{{$.root.i18n.Tr "write"}}</a>
 | 
			
		||||
			<a class="item" data-tab="preview" data-url="{{$.root.Repository.APIURL}}/markdown" data-context="{{$.root.RepoLink}}">{{$.root.i18n.Tr "preview"}}</a>
 | 
			
		||||
			<a class="item" data-tab="preview" data-url="{{$.root.Repository.HTMLURL}}/markdown" data-context="{{$.root.RepoLink}}">{{$.root.i18n.Tr "preview"}}</a>
 | 
			
		||||
		</div>
 | 
			
		||||
		<div class="field">
 | 
			
		||||
			<div class="ui active tab" data-tab="write">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,13 +31,13 @@
 | 
			
		|||
				<div class="ui top attached tabular menu" data-write="write" data-preview="preview" data-diff="diff">
 | 
			
		||||
					<a class="active item" data-tab="write">{{svg "octicon-code"}} {{if .IsNewFile}}{{.i18n.Tr "repo.editor.new_file"}}{{else}}{{.i18n.Tr "repo.editor.edit_file"}}{{end}}</a>
 | 
			
		||||
					{{if not .IsNewFile}}
 | 
			
		||||
					<a class="item" data-tab="preview" data-url="{{.Repository.APIURL}}/markdown" data-context="{{.RepoLink}}/src/{{.BranchNameSubURL}}" data-preview-file-modes="{{.PreviewableFileModes}}" data-markdown-mode="gfm">{{svg "octicon-eye"}} {{.i18n.Tr "preview"}}</a>
 | 
			
		||||
					<a class="item" data-tab="preview" data-url="{{.Repository.HTMLURL}}/markdown" data-context="{{.RepoLink}}/src/{{.BranchNameSubURL}}" data-preview-file-modes="{{.PreviewableFileModes}}" data-markdown-mode="gfm">{{svg "octicon-eye"}} {{.i18n.Tr "preview"}}</a>
 | 
			
		||||
					<a class="item" data-tab="diff" data-url="{{.RepoLink}}/_preview/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}" data-context="{{.BranchLink}}">{{svg "octicon-diff"}} {{.i18n.Tr "repo.editor.preview_changes"}}</a>
 | 
			
		||||
					{{end}}
 | 
			
		||||
				</div>
 | 
			
		||||
				<div class="ui bottom attached active tab segment" data-tab="write">
 | 
			
		||||
					<textarea id="edit_area" name="content" class="hide" data-id="repo-{{.Repository.Name}}-{{.TreePath}}"
 | 
			
		||||
						data-url="{{.Repository.APIURL}}/markdown"
 | 
			
		||||
						data-url="{{.Repository.HTMLURL}}/markdown"
 | 
			
		||||
						data-context="{{.RepoLink}}"
 | 
			
		||||
						data-markdown-file-exts="{{.MarkdownFileExts}}"
 | 
			
		||||
						data-line-wrap-extensions="{{.LineWrapExtensions}}">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +1,10 @@
 | 
			
		|||
<div class="ui top tabular menu" data-write="write" data-preview="preview">
 | 
			
		||||
	<a class="active item" data-tab="write">{{.i18n.Tr "write"}}</a>
 | 
			
		||||
	<a class="item" data-tab="preview" data-url="{{.Repository.APIURL}}/markdown" data-context="{{.RepoLink}}">{{.i18n.Tr "preview"}}</a>
 | 
			
		||||
	<a class="item" data-tab="preview" data-url="{{.Repository.HTMLURL}}/markdown" data-context="{{.RepoLink}}">{{.i18n.Tr "preview"}}</a>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="field">
 | 
			
		||||
	<div class="ui bottom active tab" data-tab="write">
 | 
			
		||||
		<textarea id="content" class="edit_area js-quick-submit" name="content" tabindex="4" data-id="issue-{{.RepoName}}" data-url="{{.Repository.APIURL}}/markdown" data-context="{{.Repo.RepoLink}}">
 | 
			
		||||
		<textarea id="content" class="edit_area js-quick-submit" name="content" tabindex="4" data-id="issue-{{.RepoName}}" data-url="{{.Repository.HTMLURL}}/markdown" data-context="{{.Repo.RepoLink}}">
 | 
			
		||||
			{{- if .BodyQuery}}{{.BodyQuery}}{{else if .IssueTemplate}}{{.IssueTemplate}}{{else if .PullRequestTemplate}}{{.PullRequestTemplate}}{{else}}{{.content}}{{end -}}
 | 
			
		||||
		</textarea>
 | 
			
		||||
	</div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -195,7 +195,7 @@
 | 
			
		|||
	<div class="ui comment form">
 | 
			
		||||
		<div class="ui top tabular menu">
 | 
			
		||||
			<a class="active write item">{{$.i18n.Tr "write"}}</a>
 | 
			
		||||
			<a class="preview item" data-url="{{$.Repository.APIURL}}/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a>
 | 
			
		||||
			<a class="preview item" data-url="{{$.Repository.HTMLURL}}/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a>
 | 
			
		||||
		</div>
 | 
			
		||||
		<div class="field">
 | 
			
		||||
			<div class="ui bottom active tab write">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,7 +48,7 @@
 | 
			
		|||
					<label>{{.i18n.Tr "repo.release.content"}}</label>
 | 
			
		||||
					<div class="ui top tabular menu" data-write="write" data-preview="preview">
 | 
			
		||||
						<a class="active write item" data-tab="write">{{$.i18n.Tr "write"}}</a>
 | 
			
		||||
						<a class="preview item" data-tab="preview" data-url="{{$.Repository.APIURL}}/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a>
 | 
			
		||||
						<a class="preview item" data-tab="preview" data-url="{{$.Repository.HTMLURL}}/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a>
 | 
			
		||||
					</div>
 | 
			
		||||
					<div class="ui bottom active tab" data-tab="write">
 | 
			
		||||
						<textarea name="content">{{.content}}</textarea>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,11 +21,11 @@
 | 
			
		|||
			</div>
 | 
			
		||||
			<div class="ui top attached tabular menu previewtabs" data-write="write" data-preview="preview">
 | 
			
		||||
				<a class="active item" data-tab="write">{{.i18n.Tr "write"}}</a>
 | 
			
		||||
				<a class="item" data-tab="preview" data-url="{{$.Repository.APIURL}}/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a>
 | 
			
		||||
				<a class="item" data-tab="preview" data-url="{{$.Repository.HTMLURL}}/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a>
 | 
			
		||||
			</div>
 | 
			
		||||
			<div class="field content" data-loading="{{.i18n.Tr "loading"}}">
 | 
			
		||||
				<div class="ui bottom active tab" data-tab="write">
 | 
			
		||||
					<textarea class="js-quick-submit" id="edit_area" name="content" data-id="wiki-{{.title}}" data-url="{{.Repository.APIURL}}/markdown" data-context="{{.RepoLink}}">{{if .PageIsWikiEdit}}{{.content}}{{else}}{{.i18n.Tr "repo.wiki.welcome"}}{{end}}</textarea>
 | 
			
		||||
					<textarea class="js-quick-submit" id="edit_area" name="content" data-id="wiki-{{.title}}" data-url="{{.Repository.HTMLURL}}/markdown" data-context="{{.RepoLink}}">{{if .PageIsWikiEdit}}{{.content}}{{else}}{{.i18n.Tr "repo.wiki.welcome"}}{{end}}</textarea>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
			<div class="field">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue