mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	Fix PR web route permission check (#33636)
See the FIXME comment in code. Otherwise, if a repo's issue unit is disabled, then the PRs can't be edited anymore. By the way, make the permission log output look slightly better. --------- Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: metiftikci <metiftikci@hotmail.com>
This commit is contained in:
		| @@ -152,7 +152,7 @@ func (p *Permission) ReadableUnitTypes() []unit.Type { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (p *Permission) LogString() string { | func (p *Permission) LogString() string { | ||||||
| 	format := "<Permission AccessMode=%s, %d Units, %d UnitsMode(s): [ " | 	format := "<Permission AccessMode=%s, %d Units, %d UnitsMode(s): [" | ||||||
| 	args := []any{p.AccessMode.ToString(), len(p.units), len(p.unitsMode)} | 	args := []any{p.AccessMode.ToString(), len(p.units), len(p.unitsMode)} | ||||||
|  |  | ||||||
| 	for i, u := range p.units { | 	for i, u := range p.units { | ||||||
| @@ -164,14 +164,16 @@ func (p *Permission) LogString() string { | |||||||
| 				config = err.Error() | 				config = err.Error() | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		format += "\nUnits[%d]: ID: %d RepoID: %d Type: %s Config: %s" | 		format += "\n\tunits[%d]: ID=%d RepoID=%d Type=%s Config=%s" | ||||||
| 		args = append(args, i, u.ID, u.RepoID, u.Type.LogString(), config) | 		args = append(args, i, u.ID, u.RepoID, u.Type.LogString(), config) | ||||||
| 	} | 	} | ||||||
| 	for key, value := range p.unitsMode { | 	for key, value := range p.unitsMode { | ||||||
| 		format += "\nUnitMode[%-v]: %-v" | 		format += "\n\tunitsMode[%-v]: %-v" | ||||||
| 		args = append(args, key.LogString(), value.LogString()) | 		args = append(args, key.LogString(), value.LogString()) | ||||||
| 	} | 	} | ||||||
| 	format += " ]>" | 	format += "\n\teveryoneAccessMode: %-v" | ||||||
|  | 	args = append(args, p.everyoneAccessMode) | ||||||
|  | 	format += "\n\t]>" | ||||||
| 	return fmt.Sprintf(format, args...) | 	return fmt.Sprintf(format, args...) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ package log | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
|  | 	"reflect" | ||||||
| 	"runtime" | 	"runtime" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"sync" | 	"sync" | ||||||
| @@ -175,6 +176,20 @@ func (l *LoggerImpl) IsEnabled() bool { | |||||||
| 	return l.level.Load() < int32(FATAL) && len(l.eventWriters) > 0 | 	return l.level.Load() < int32(FATAL) && len(l.eventWriters) > 0 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func asLogStringer(v any) LogStringer { | ||||||
|  | 	if s, ok := v.(LogStringer); ok { | ||||||
|  | 		return s | ||||||
|  | 	} else if a := reflect.ValueOf(v); a.Kind() == reflect.Struct { | ||||||
|  | 		// in case the receiver is a pointer, but the value is a struct | ||||||
|  | 		vp := reflect.New(a.Type()) | ||||||
|  | 		vp.Elem().Set(a) | ||||||
|  | 		if s, ok := vp.Interface().(LogStringer); ok { | ||||||
|  | 			return s | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
| // Log prepares the log event, if the level matches, the event will be sent to the writers | // Log prepares the log event, if the level matches, the event will be sent to the writers | ||||||
| func (l *LoggerImpl) Log(skip int, level Level, format string, logArgs ...any) { | func (l *LoggerImpl) Log(skip int, level Level, format string, logArgs ...any) { | ||||||
| 	if Level(l.level.Load()) > level { | 	if Level(l.level.Load()) > level { | ||||||
| @@ -207,11 +222,11 @@ func (l *LoggerImpl) Log(skip int, level Level, format string, logArgs ...any) { | |||||||
| 	// handle LogStringer values | 	// handle LogStringer values | ||||||
| 	for i, v := range msgArgs { | 	for i, v := range msgArgs { | ||||||
| 		if cv, ok := v.(*ColoredValue); ok { | 		if cv, ok := v.(*ColoredValue); ok { | ||||||
| 			if s, ok := cv.v.(LogStringer); ok { | 			if ls := asLogStringer(cv.v); ls != nil { | ||||||
| 				cv.v = logStringFormatter{v: s} | 				cv.v = logStringFormatter{v: ls} | ||||||
| 			} | 			} | ||||||
| 		} else if s, ok := v.(LogStringer); ok { | 		} else if ls := asLogStringer(v); ls != nil { | ||||||
| 			msgArgs[i] = logStringFormatter{v: s} | 			msgArgs[i] = logStringFormatter{v: ls} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -116,6 +116,14 @@ func (t testLogString) LogString() string { | |||||||
| 	return "log-string" | 	return "log-string" | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type testLogStringPtrReceiver struct { | ||||||
|  | 	Field string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *testLogStringPtrReceiver) LogString() string { | ||||||
|  | 	return "log-string-ptr-receiver" | ||||||
|  | } | ||||||
|  |  | ||||||
| func TestLoggerLogString(t *testing.T) { | func TestLoggerLogString(t *testing.T) { | ||||||
| 	logger := NewLoggerWithWriters(context.Background(), "test") | 	logger := NewLoggerWithWriters(context.Background(), "test") | ||||||
|  |  | ||||||
| @@ -124,9 +132,13 @@ func TestLoggerLogString(t *testing.T) { | |||||||
| 	logger.AddWriters(w1) | 	logger.AddWriters(w1) | ||||||
|  |  | ||||||
| 	logger.Info("%s %s %#v %v", testLogString{}, &testLogString{}, testLogString{Field: "detail"}, NewColoredValue(testLogString{}, FgRed)) | 	logger.Info("%s %s %#v %v", testLogString{}, &testLogString{}, testLogString{Field: "detail"}, NewColoredValue(testLogString{}, FgRed)) | ||||||
|  | 	logger.Info("%s %s %#v %v", testLogStringPtrReceiver{}, &testLogStringPtrReceiver{}, testLogStringPtrReceiver{Field: "detail"}, NewColoredValue(testLogStringPtrReceiver{}, FgRed)) | ||||||
| 	logger.Close() | 	logger.Close() | ||||||
|  |  | ||||||
| 	assert.Equal(t, []string{"log-string log-string log.testLogString{Field:\"detail\"} \x1b[31mlog-string\x1b[0m\n"}, w1.GetLogs()) | 	assert.Equal(t, []string{ | ||||||
|  | 		"log-string log-string log.testLogString{Field:\"detail\"} \x1b[31mlog-string\x1b[0m\n", | ||||||
|  | 		"log-string-ptr-receiver log-string-ptr-receiver &log.testLogStringPtrReceiver{Field:\"detail\"} \x1b[31mlog-string-ptr-receiver\x1b[0m\n", | ||||||
|  | 	}, w1.GetLogs()) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestLoggerExpressionFilter(t *testing.T) { | func TestLoggerExpressionFilter(t *testing.T) { | ||||||
|   | |||||||
| @@ -1196,6 +1196,10 @@ func registerRoutes(m *web.Router) { | |||||||
| 			}) | 			}) | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
|  | 	// FIXME: many "pulls" requests are sent to "issues" endpoints correctly, so the issue endpoints have to tolerate pull request permissions at the moment | ||||||
|  | 	m.Group("/{username}/{reponame}/{type:issues}", addIssuesPullsViewRoutes, optSignIn, context.RepoAssignment, context.RequireUnitReader(unit.TypeIssues, unit.TypePullRequests)) | ||||||
|  | 	m.Group("/{username}/{reponame}/{type:pulls}", addIssuesPullsViewRoutes, optSignIn, context.RepoAssignment, reqUnitPullsReader) | ||||||
|  |  | ||||||
| 	m.Group("/{username}/{reponame}", func() { | 	m.Group("/{username}/{reponame}", func() { | ||||||
| 		m.Get("/comments/{id}/attachments", repo.GetCommentAttachments) | 		m.Get("/comments/{id}/attachments", repo.GetCommentAttachments) | ||||||
| 		m.Get("/labels", repo.RetrieveLabelsForList, repo.Labels) | 		m.Get("/labels", repo.RetrieveLabelsForList, repo.Labels) | ||||||
| @@ -1203,9 +1207,6 @@ func registerRoutes(m *web.Router) { | |||||||
| 		m.Get("/milestone/{id}", context.RepoRef(), repo.MilestoneIssuesAndPulls) | 		m.Get("/milestone/{id}", context.RepoRef(), repo.MilestoneIssuesAndPulls) | ||||||
| 		m.Get("/issues/suggestions", repo.IssueSuggestions) | 		m.Get("/issues/suggestions", repo.IssueSuggestions) | ||||||
| 	}, optSignIn, context.RepoAssignment, reqRepoIssuesOrPullsReader) // issue/pull attachments, labels, milestones | 	}, optSignIn, context.RepoAssignment, reqRepoIssuesOrPullsReader) // issue/pull attachments, labels, milestones | ||||||
|  |  | ||||||
| 	m.Group("/{username}/{reponame}/{type:issues}", addIssuesPullsViewRoutes, optSignIn, context.RepoAssignment, reqUnitIssuesReader) |  | ||||||
| 	m.Group("/{username}/{reponame}/{type:pulls}", addIssuesPullsViewRoutes, optSignIn, context.RepoAssignment, reqUnitPullsReader) |  | ||||||
| 	// end "/{username}/{reponame}": view milestone, label, issue, pull, etc | 	// end "/{username}/{reponame}": view milestone, label, issue, pull, etc | ||||||
|  |  | ||||||
| 	m.Group("/{username}/{reponame}/{type:issues}", func() { | 	m.Group("/{username}/{reponame}/{type:issues}", func() { | ||||||
| @@ -1224,7 +1225,7 @@ func registerRoutes(m *web.Router) { | |||||||
| 			m.Get("/search", repo.SearchRepoIssuesJSON) | 			m.Get("/search", repo.SearchRepoIssuesJSON) | ||||||
| 		}, reqUnitIssuesReader) | 		}, reqUnitIssuesReader) | ||||||
|  |  | ||||||
| 		addIssuesPullsRoutes := func() { | 		addIssuesPullsUpdateRoutes := func() { | ||||||
| 			// for "/{username}/{reponame}/issues" or "/{username}/{reponame}/pulls" | 			// for "/{username}/{reponame}/issues" or "/{username}/{reponame}/pulls" | ||||||
| 			m.Group("/{index}", func() { | 			m.Group("/{index}", func() { | ||||||
| 				m.Post("/title", repo.UpdateIssueTitle) | 				m.Post("/title", repo.UpdateIssueTitle) | ||||||
| @@ -1267,8 +1268,9 @@ func registerRoutes(m *web.Router) { | |||||||
| 			m.Delete("/unpin/{index}", reqRepoAdmin, repo.IssueUnpin) | 			m.Delete("/unpin/{index}", reqRepoAdmin, repo.IssueUnpin) | ||||||
| 			m.Post("/move_pin", reqRepoAdmin, repo.IssuePinMove) | 			m.Post("/move_pin", reqRepoAdmin, repo.IssuePinMove) | ||||||
| 		} | 		} | ||||||
| 		m.Group("/{type:issues}", addIssuesPullsRoutes, reqUnitIssuesReader, context.RepoMustNotBeArchived()) | 		// FIXME: many "pulls" requests are sent to "issues" endpoints incorrectly, so the issue endpoints have to tolerate pull request permissions at the moment | ||||||
| 		m.Group("/{type:pulls}", addIssuesPullsRoutes, reqUnitPullsReader, context.RepoMustNotBeArchived()) | 		m.Group("/{type:issues}", addIssuesPullsUpdateRoutes, context.RequireUnitReader(unit.TypeIssues, unit.TypePullRequests), context.RepoMustNotBeArchived()) | ||||||
|  | 		m.Group("/{type:pulls}", addIssuesPullsUpdateRoutes, reqUnitPullsReader, context.RepoMustNotBeArchived()) | ||||||
|  |  | ||||||
| 		m.Group("/comments/{id}", func() { | 		m.Group("/comments/{id}", func() { | ||||||
| 			m.Post("", repo.UpdateCommentContent) | 			m.Post("", repo.UpdateCommentContent) | ||||||
| @@ -1292,7 +1294,7 @@ func registerRoutes(m *web.Router) { | |||||||
| 			m.Post("/delete", repo.DeleteMilestone) | 			m.Post("/delete", repo.DeleteMilestone) | ||||||
| 		}, reqRepoIssuesOrPullsWriter, context.RepoRef()) | 		}, reqRepoIssuesOrPullsWriter, context.RepoRef()) | ||||||
|  |  | ||||||
| 		// FIXME: need to move these routes to the proper place | 		// FIXME: many "pulls" requests are sent to "issues" endpoints incorrectly, need to move these routes to the proper place | ||||||
| 		m.Group("/issues", func() { | 		m.Group("/issues", func() { | ||||||
| 			m.Post("/request_review", repo.UpdatePullReviewRequest) | 			m.Post("/request_review", repo.UpdatePullReviewRequest) | ||||||
| 			m.Post("/dismiss_review", reqRepoAdmin, web.Bind(forms.DismissReviewForm{}), repo.DismissReview) | 			m.Post("/dismiss_review", reqRepoAdmin, web.Bind(forms.DismissReviewForm{}), repo.DismissReview) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user