mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 22:28:05 +01:00 
			
		
		
		
	Fix LFS route mock, realm, middleware names (#32488)
1. move "internal-lfs" route mock to "common-lfs" 2. fine tune tests 3. fix "realm" strings, according to RFC: https://datatracker.ietf.org/doc/html/rfc2617: * realm = "realm" "=" realm-value * realm-value = quoted-string 4. clarify some names of the middlewares, rename `ignXxx` to `optXxx` to match `reqXxx`, and rename ambiguous `requireSignIn` to `reqGitSignIn`
This commit is contained in:
		
							parent
							
								
									840ad7eefe
								
							
						
					
					
						commit
						0aedb03996
					
				
							
								
								
									
										1
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								go.mod
									
									
									
									
									
								
							| @ -330,6 +330,7 @@ replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142 | |||||||
| 
 | 
 | ||||||
| replace github.com/nektos/act => gitea.com/gitea/act v0.261.3 | replace github.com/nektos/act => gitea.com/gitea/act v0.261.3 | ||||||
| 
 | 
 | ||||||
|  | // TODO: the only difference is in `PutObject`: the fork doesn't use `NewVerifyingReader(r, sha256.New(), oid, expectedSize)`, need to figure out why | ||||||
| replace github.com/charmbracelet/git-lfs-transfer => gitea.com/gitea/git-lfs-transfer v0.2.0 | replace github.com/charmbracelet/git-lfs-transfer => gitea.com/gitea/git-lfs-transfer v0.2.0 | ||||||
| 
 | 
 | ||||||
| // TODO: This could be removed after https://github.com/mholt/archiver/pull/396 merged | // TODO: This could be removed after https://github.com/mholt/archiver/pull/396 merged | ||||||
|  | |||||||
| @ -10,6 +10,8 @@ import ( | |||||||
| 	"code.gitea.io/gitea/services/lfs" | 	"code.gitea.io/gitea/services/lfs" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | const RouterMockPointCommonLFS = "common-lfs" | ||||||
|  | 
 | ||||||
| func AddOwnerRepoGitLFSRoutes(m *web.Router, middlewares ...any) { | func AddOwnerRepoGitLFSRoutes(m *web.Router, middlewares ...any) { | ||||||
| 	// shared by web and internal routers | 	// shared by web and internal routers | ||||||
| 	m.Group("/{username}/{reponame}/info/lfs", func() { | 	m.Group("/{username}/{reponame}/info/lfs", func() { | ||||||
| @ -25,5 +27,5 @@ func AddOwnerRepoGitLFSRoutes(m *web.Router, middlewares ...any) { | |||||||
| 			m.Post("/{lid}/unlock", lfs.UnLockHandler) | 			m.Post("/{lid}/unlock", lfs.UnLockHandler) | ||||||
| 		}, lfs.CheckAcceptMediaType) | 		}, lfs.CheckAcceptMediaType) | ||||||
| 		m.Any("/*", http.NotFound) | 		m.Any("/*", http.NotFound) | ||||||
| 	}, middlewares...) | 	}, append([]any{web.RouterMockPoint(RouterMockPointCommonLFS)}, middlewares...)...) | ||||||
| } | } | ||||||
|  | |||||||
| @ -20,8 +20,6 @@ import ( | |||||||
| 	chi_middleware "github.com/go-chi/chi/v5/middleware" | 	chi_middleware "github.com/go-chi/chi/v5/middleware" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const RouterMockPointInternalLFS = "internal-lfs" |  | ||||||
| 
 |  | ||||||
| func authInternal(next http.Handler) http.Handler { | func authInternal(next http.Handler) http.Handler { | ||||||
| 	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | 	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | ||||||
| 		if setting.InternalToken == "" { | 		if setting.InternalToken == "" { | ||||||
| @ -87,10 +85,11 @@ func Routes() *web.Router { | |||||||
| 
 | 
 | ||||||
| 	r.Group("/repo", func() { | 	r.Group("/repo", func() { | ||||||
| 		// FIXME: it is not right to use context.Contexter here because all routes here should use PrivateContext | 		// FIXME: it is not right to use context.Contexter here because all routes here should use PrivateContext | ||||||
|  | 		// Fortunately, the LFS handlers are able to handle requests without a complete web context | ||||||
| 		common.AddOwnerRepoGitLFSRoutes(r, func(ctx *context.PrivateContext) { | 		common.AddOwnerRepoGitLFSRoutes(r, func(ctx *context.PrivateContext) { | ||||||
| 			webContext := &context.Context{Base: ctx.Base} | 			webContext := &context.Context{Base: ctx.Base} | ||||||
| 			ctx.AppendContextValue(context.WebContextKey, webContext) | 			ctx.AppendContextValue(context.WebContextKey, webContext) | ||||||
| 		}, web.RouterMockPoint(RouterMockPointInternalLFS)) | 		}) | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	return r | 	return r | ||||||
|  | |||||||
| @ -91,7 +91,7 @@ type userInfoResponse struct { | |||||||
| // InfoOAuth manages request for userinfo endpoint | // InfoOAuth manages request for userinfo endpoint | ||||||
| func InfoOAuth(ctx *context.Context) { | func InfoOAuth(ctx *context.Context) { | ||||||
| 	if ctx.Doer == nil || ctx.Data["AuthedMethod"] != (&auth_service.OAuth2{}).Name() { | 	if ctx.Doer == nil || ctx.Data["AuthedMethod"] != (&auth_service.OAuth2{}).Name() { | ||||||
| 		ctx.Resp.Header().Set("WWW-Authenticate", `Bearer realm=""`) | 		ctx.Resp.Header().Set("WWW-Authenticate", `Bearer realm="Gitea OAuth2"`) | ||||||
| 		ctx.PlainText(http.StatusUnauthorized, "no valid authorization") | 		ctx.PlainText(http.StatusUnauthorized, "no valid authorization") | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| @ -136,7 +136,7 @@ func IntrospectOAuth(ctx *context.Context) { | |||||||
| 		clientIDValid = err == nil && app.ValidateClientSecret([]byte(clientSecret)) | 		clientIDValid = err == nil && app.ValidateClientSecret([]byte(clientSecret)) | ||||||
| 	} | 	} | ||||||
| 	if !clientIDValid { | 	if !clientIDValid { | ||||||
| 		ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm=""`) | 		ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="Gitea OAuth2"`) | ||||||
| 		ctx.PlainText(http.StatusUnauthorized, "no valid authorization") | 		ctx.PlainText(http.StatusUnauthorized, "no valid authorization") | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -12,21 +12,19 @@ import ( | |||||||
| 	"code.gitea.io/gitea/services/context" | 	"code.gitea.io/gitea/services/context" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func requireSignIn(ctx *context.Context) { | func addOwnerRepoGitHTTPRouters(m *web.Router) { | ||||||
| 	if !setting.Service.RequireSignInView { | 	reqGitSignIn := func(ctx *context.Context) { | ||||||
| 		return | 		if !setting.Service.RequireSignInView { | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		// rely on the results of Contexter | ||||||
|  | 		if !ctx.IsSigned { | ||||||
|  | 			// TODO: support digit auth - which would be Authorization header with digit | ||||||
|  | 			ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="Gitea"`) | ||||||
|  | 			ctx.Error(http.StatusUnauthorized) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 	m.Group("/{username}/{reponame}", func() { | ||||||
| 	// rely on the results of Contexter |  | ||||||
| 	if !ctx.IsSigned { |  | ||||||
| 		// TODO: support digit auth - which would be Authorization header with digit |  | ||||||
| 		ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="Gitea"`) |  | ||||||
| 		ctx.Error(http.StatusUnauthorized) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func gitHTTPRouters(m *web.Router) { |  | ||||||
| 	m.Group("", func() { |  | ||||||
| 		m.Methods("POST,OPTIONS", "/git-upload-pack", repo.ServiceUploadPack) | 		m.Methods("POST,OPTIONS", "/git-upload-pack", repo.ServiceUploadPack) | ||||||
| 		m.Methods("POST,OPTIONS", "/git-receive-pack", repo.ServiceReceivePack) | 		m.Methods("POST,OPTIONS", "/git-receive-pack", repo.ServiceReceivePack) | ||||||
| 		m.Methods("GET,OPTIONS", "/info/refs", repo.GetInfoRefs) | 		m.Methods("GET,OPTIONS", "/info/refs", repo.GetInfoRefs) | ||||||
| @ -38,5 +36,5 @@ func gitHTTPRouters(m *web.Router) { | |||||||
| 		m.Methods("GET,OPTIONS", "/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38,62}}", repo.GetLooseObject) | 		m.Methods("GET,OPTIONS", "/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38,62}}", repo.GetLooseObject) | ||||||
| 		m.Methods("GET,OPTIONS", "/objects/pack/pack-{file:[0-9a-f]{40,64}}.pack", repo.GetPackFile) | 		m.Methods("GET,OPTIONS", "/objects/pack/pack-{file:[0-9a-f]{40,64}}.pack", repo.GetPackFile) | ||||||
| 		m.Methods("GET,OPTIONS", "/objects/pack/pack-{file:[0-9a-f]{40,64}}.idx", repo.GetIdxFile) | 		m.Methods("GET,OPTIONS", "/objects/pack/pack-{file:[0-9a-f]{40,64}}.idx", repo.GetIdxFile) | ||||||
| 	}, ignSignInAndCsrf, requireSignIn, repo.HTTPGitEnabledHandler, repo.CorsHandler(), context.UserAssignmentWeb()) | 	}, optSignInIgnoreCsrf, reqGitSignIn, repo.HTTPGitEnabledHandler, repo.CorsHandler(), context.UserAssignmentWeb()) | ||||||
| } | } | ||||||
|  | |||||||
| @ -291,15 +291,16 @@ func Routes() *web.Router { | |||||||
| 	return routes | 	return routes | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var ignSignInAndCsrf = verifyAuthWithOptions(&common.VerifyOptions{DisableCSRF: true}) | var optSignInIgnoreCsrf = verifyAuthWithOptions(&common.VerifyOptions{DisableCSRF: true}) | ||||||
| 
 | 
 | ||||||
| // registerRoutes register routes | // registerRoutes register routes | ||||||
| func registerRoutes(m *web.Router) { | func registerRoutes(m *web.Router) { | ||||||
|  | 	// required to be signed in or signed out | ||||||
| 	reqSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: true}) | 	reqSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: true}) | ||||||
| 	reqSignOut := verifyAuthWithOptions(&common.VerifyOptions{SignOutRequired: true}) | 	reqSignOut := verifyAuthWithOptions(&common.VerifyOptions{SignOutRequired: true}) | ||||||
| 	// TODO: rename them to "optSignIn", which means that the "sign-in" could be optional, depends on the VerifyOptions (RequireSignInView) | 	// optional sign in (if signed in, use the user as doer, if not, no doer) | ||||||
| 	ignSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: setting.Service.RequireSignInView}) | 	optSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: setting.Service.RequireSignInView}) | ||||||
| 	ignExploreSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: setting.Service.RequireSignInView || setting.Service.Explore.RequireSigninView}) | 	optExploreSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: setting.Service.RequireSignInView || setting.Service.Explore.RequireSigninView}) | ||||||
| 
 | 
 | ||||||
| 	validation.AddBindingRules() | 	validation.AddBindingRules() | ||||||
| 
 | 
 | ||||||
| @ -470,7 +471,7 @@ func registerRoutes(m *web.Router) { | |||||||
| 	// Especially some AJAX requests, we can reduce middleware number to improve performance. | 	// Especially some AJAX requests, we can reduce middleware number to improve performance. | ||||||
| 
 | 
 | ||||||
| 	m.Get("/", Home) | 	m.Get("/", Home) | ||||||
| 	m.Get("/sitemap.xml", sitemapEnabled, ignExploreSignIn, HomeSitemap) | 	m.Get("/sitemap.xml", sitemapEnabled, optExploreSignIn, HomeSitemap) | ||||||
| 	m.Group("/.well-known", func() { | 	m.Group("/.well-known", func() { | ||||||
| 		m.Get("/openid-configuration", auth.OIDCWellKnown) | 		m.Get("/openid-configuration", auth.OIDCWellKnown) | ||||||
| 		m.Group("", func() { | 		m.Group("", func() { | ||||||
| @ -500,7 +501,7 @@ func registerRoutes(m *web.Router) { | |||||||
| 			} | 			} | ||||||
| 		}, explore.Code) | 		}, explore.Code) | ||||||
| 		m.Get("/topics/search", explore.TopicSearch) | 		m.Get("/topics/search", explore.TopicSearch) | ||||||
| 	}, ignExploreSignIn) | 	}, optExploreSignIn) | ||||||
| 
 | 
 | ||||||
| 	m.Group("/issues", func() { | 	m.Group("/issues", func() { | ||||||
| 		m.Get("", user.Issues) | 		m.Get("", user.Issues) | ||||||
| @ -558,12 +559,12 @@ func registerRoutes(m *web.Router) { | |||||||
| 			m.Post("/grant", web.Bind(forms.GrantApplicationForm{}), auth.GrantApplicationOAuth) | 			m.Post("/grant", web.Bind(forms.GrantApplicationForm{}), auth.GrantApplicationOAuth) | ||||||
| 			// TODO manage redirection | 			// TODO manage redirection | ||||||
| 			m.Post("/authorize", web.Bind(forms.AuthorizationForm{}), auth.AuthorizeOAuth) | 			m.Post("/authorize", web.Bind(forms.AuthorizationForm{}), auth.AuthorizeOAuth) | ||||||
| 		}, ignSignInAndCsrf, reqSignIn) | 		}, optSignInIgnoreCsrf, reqSignIn) | ||||||
| 
 | 
 | ||||||
| 		m.Methods("GET, OPTIONS", "/userinfo", optionsCorsHandler(), ignSignInAndCsrf, auth.InfoOAuth) | 		m.Methods("GET, OPTIONS", "/userinfo", optionsCorsHandler(), optSignInIgnoreCsrf, auth.InfoOAuth) | ||||||
| 		m.Methods("POST, OPTIONS", "/access_token", optionsCorsHandler(), web.Bind(forms.AccessTokenForm{}), ignSignInAndCsrf, auth.AccessTokenOAuth) | 		m.Methods("POST, OPTIONS", "/access_token", optionsCorsHandler(), web.Bind(forms.AccessTokenForm{}), optSignInIgnoreCsrf, auth.AccessTokenOAuth) | ||||||
| 		m.Methods("GET, OPTIONS", "/keys", optionsCorsHandler(), ignSignInAndCsrf, auth.OIDCKeys) | 		m.Methods("GET, OPTIONS", "/keys", optionsCorsHandler(), optSignInIgnoreCsrf, auth.OIDCKeys) | ||||||
| 		m.Methods("POST, OPTIONS", "/introspect", optionsCorsHandler(), web.Bind(forms.IntrospectTokenForm{}), ignSignInAndCsrf, auth.IntrospectOAuth) | 		m.Methods("POST, OPTIONS", "/introspect", optionsCorsHandler(), web.Bind(forms.IntrospectTokenForm{}), optSignInIgnoreCsrf, auth.IntrospectOAuth) | ||||||
| 	}, oauth2Enabled) | 	}, oauth2Enabled) | ||||||
| 
 | 
 | ||||||
| 	m.Group("/user/settings", func() { | 	m.Group("/user/settings", func() { | ||||||
| @ -685,7 +686,7 @@ func registerRoutes(m *web.Router) { | |||||||
| 		m.Post("/forgot_password", auth.ForgotPasswdPost) | 		m.Post("/forgot_password", auth.ForgotPasswdPost) | ||||||
| 		m.Post("/logout", auth.SignOut) | 		m.Post("/logout", auth.SignOut) | ||||||
| 		m.Get("/stopwatches", reqSignIn, user.GetStopwatches) | 		m.Get("/stopwatches", reqSignIn, user.GetStopwatches) | ||||||
| 		m.Get("/search_candidates", ignExploreSignIn, user.SearchCandidates) | 		m.Get("/search_candidates", optExploreSignIn, user.SearchCandidates) | ||||||
| 		m.Group("/oauth2", func() { | 		m.Group("/oauth2", func() { | ||||||
| 			m.Get("/{provider}", auth.SignInOAuth) | 			m.Get("/{provider}", auth.SignInOAuth) | ||||||
| 			m.Get("/{provider}/callback", auth.SignInOAuthCallback) | 			m.Get("/{provider}/callback", auth.SignInOAuthCallback) | ||||||
| @ -809,7 +810,7 @@ func registerRoutes(m *web.Router) { | |||||||
| 	m.Group("", func() { | 	m.Group("", func() { | ||||||
| 		m.Get("/{username}", user.UsernameSubRoute) | 		m.Get("/{username}", user.UsernameSubRoute) | ||||||
| 		m.Methods("GET, OPTIONS", "/attachments/{uuid}", optionsCorsHandler(), repo.GetAttachment) | 		m.Methods("GET, OPTIONS", "/attachments/{uuid}", optionsCorsHandler(), repo.GetAttachment) | ||||||
| 	}, ignSignIn) | 	}, optSignIn) | ||||||
| 
 | 
 | ||||||
| 	m.Post("/{username}", reqSignIn, context.UserAssignmentWeb(), user.Action) | 	m.Post("/{username}", reqSignIn, context.UserAssignmentWeb(), user.Action) | ||||||
| 
 | 
 | ||||||
| @ -860,7 +861,7 @@ func registerRoutes(m *web.Router) { | |||||||
| 		m.Group("/{org}", func() { | 		m.Group("/{org}", func() { | ||||||
| 			m.Get("/members", org.Members) | 			m.Get("/members", org.Members) | ||||||
| 		}, context.OrgAssignment()) | 		}, context.OrgAssignment()) | ||||||
| 	}, ignSignIn) | 	}, optSignIn) | ||||||
| 	// end "/org": members | 	// end "/org": members | ||||||
| 
 | 
 | ||||||
| 	m.Group("/org", func() { | 	m.Group("/org", func() { | ||||||
| @ -1043,14 +1044,14 @@ func registerRoutes(m *web.Router) { | |||||||
| 		m.Group("", func() { | 		m.Group("", func() { | ||||||
| 			m.Get("/code", user.CodeSearch) | 			m.Get("/code", user.CodeSearch) | ||||||
| 		}, reqUnitAccess(unit.TypeCode, perm.AccessModeRead, false), individualPermsChecker) | 		}, reqUnitAccess(unit.TypeCode, perm.AccessModeRead, false), individualPermsChecker) | ||||||
| 	}, ignSignIn, context.UserAssignmentWeb(), context.OrgAssignment()) | 	}, optSignIn, context.UserAssignmentWeb(), context.OrgAssignment()) | ||||||
| 	// end "/{username}/-": packages, projects, code | 	// end "/{username}/-": packages, projects, code | ||||||
| 
 | 
 | ||||||
| 	m.Group("/{username}/{reponame}/-", func() { | 	m.Group("/{username}/{reponame}/-", func() { | ||||||
| 		m.Group("/migrate", func() { | 		m.Group("/migrate", func() { | ||||||
| 			m.Get("/status", repo.MigrateStatus) | 			m.Get("/status", repo.MigrateStatus) | ||||||
| 		}) | 		}) | ||||||
| 	}, ignSignIn, context.RepoAssignment, reqRepoCodeReader) | 	}, optSignIn, context.RepoAssignment, reqRepoCodeReader) | ||||||
| 	// end "/{username}/{reponame}/-": migrate | 	// end "/{username}/{reponame}/-": migrate | ||||||
| 
 | 
 | ||||||
| 	m.Group("/{username}/{reponame}/settings", func() { | 	m.Group("/{username}/{reponame}/settings", func() { | ||||||
| @ -1145,10 +1146,10 @@ func registerRoutes(m *web.Router) { | |||||||
| 	// end "/{username}/{reponame}/settings" | 	// end "/{username}/{reponame}/settings" | ||||||
| 
 | 
 | ||||||
| 	// user/org home, including rss feeds | 	// user/org home, including rss feeds | ||||||
| 	m.Get("/{username}/{reponame}", ignSignIn, context.RepoAssignment, context.RepoRef(), repo.SetEditorconfigIfExists, repo.Home) | 	m.Get("/{username}/{reponame}", optSignIn, context.RepoAssignment, context.RepoRef(), repo.SetEditorconfigIfExists, repo.Home) | ||||||
| 
 | 
 | ||||||
| 	// TODO: maybe it should relax the permission to allow "any access" | 	// TODO: maybe it should relax the permission to allow "any access" | ||||||
| 	m.Post("/{username}/{reponame}/markup", ignSignIn, context.RepoAssignment, context.RequireRepoReaderOr(unit.TypeCode, unit.TypeIssues, unit.TypePullRequests, unit.TypeReleases, unit.TypeWiki), web.Bind(structs.MarkupOption{}), misc.Markup) | 	m.Post("/{username}/{reponame}/markup", optSignIn, context.RepoAssignment, context.RequireRepoReaderOr(unit.TypeCode, unit.TypeIssues, unit.TypePullRequests, unit.TypeReleases, unit.TypeWiki), web.Bind(structs.MarkupOption{}), misc.Markup) | ||||||
| 
 | 
 | ||||||
| 	m.Group("/{username}/{reponame}", func() { | 	m.Group("/{username}/{reponame}", func() { | ||||||
| 		m.Get("/find/*", repo.FindFiles) | 		m.Get("/find/*", repo.FindFiles) | ||||||
| @ -1161,7 +1162,7 @@ func registerRoutes(m *web.Router) { | |||||||
| 		m.Combo("/compare/*", repo.MustBeNotEmpty, repo.SetEditorconfigIfExists). | 		m.Combo("/compare/*", repo.MustBeNotEmpty, repo.SetEditorconfigIfExists). | ||||||
| 			Get(repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff). | 			Get(repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff). | ||||||
| 			Post(reqSignIn, context.RepoMustNotBeArchived(), reqRepoPullsReader, repo.MustAllowPulls, web.Bind(forms.CreateIssueForm{}), repo.SetWhitespaceBehavior, repo.CompareAndPullRequestPost) | 			Post(reqSignIn, context.RepoMustNotBeArchived(), reqRepoPullsReader, repo.MustAllowPulls, web.Bind(forms.CreateIssueForm{}), repo.SetWhitespaceBehavior, repo.CompareAndPullRequestPost) | ||||||
| 	}, ignSignIn, context.RepoAssignment, reqRepoCodeReader) | 	}, optSignIn, context.RepoAssignment, reqRepoCodeReader) | ||||||
| 	// end "/{username}/{reponame}": find, compare, list (code related) | 	// end "/{username}/{reponame}": find, compare, list (code related) | ||||||
| 
 | 
 | ||||||
| 	m.Group("/{username}/{reponame}", func() { | 	m.Group("/{username}/{reponame}", func() { | ||||||
| @ -1184,7 +1185,7 @@ func registerRoutes(m *web.Router) { | |||||||
| 			}) | 			}) | ||||||
| 		}, context.RepoRef()) | 		}, context.RepoRef()) | ||||||
| 		m.Get("/issues/suggestions", repo.IssueSuggestions) | 		m.Get("/issues/suggestions", repo.IssueSuggestions) | ||||||
| 	}, ignSignIn, context.RepoAssignment, reqRepoIssuesOrPullsReader) | 	}, optSignIn, context.RepoAssignment, reqRepoIssuesOrPullsReader) | ||||||
| 	// end "/{username}/{reponame}": view milestone, label, issue, pull, etc | 	// end "/{username}/{reponame}": view milestone, label, issue, pull, etc | ||||||
| 
 | 
 | ||||||
| 	m.Group("/{username}/{reponame}", func() { | 	m.Group("/{username}/{reponame}", func() { | ||||||
| @ -1194,7 +1195,7 @@ func registerRoutes(m *web.Router) { | |||||||
| 				m.Get("", repo.ViewIssue) | 				m.Get("", repo.ViewIssue) | ||||||
| 			}) | 			}) | ||||||
| 		}) | 		}) | ||||||
| 	}, ignSignIn, context.RepoAssignment, context.RequireRepoReaderOr(unit.TypeIssues, unit.TypePullRequests, unit.TypeExternalTracker)) | 	}, optSignIn, context.RepoAssignment, context.RequireRepoReaderOr(unit.TypeIssues, unit.TypePullRequests, unit.TypeExternalTracker)) | ||||||
| 	// end "/{username}/{reponame}": issue/pull list, issue/pull view, external tracker | 	// end "/{username}/{reponame}": issue/pull list, issue/pull view, external tracker | ||||||
| 
 | 
 | ||||||
| 	m.Group("/{username}/{reponame}", func() { // edit issues, pulls, labels, milestones, etc | 	m.Group("/{username}/{reponame}", func() { // edit issues, pulls, labels, milestones, etc | ||||||
| @ -1331,7 +1332,7 @@ func registerRoutes(m *web.Router) { | |||||||
| 			repo.MustBeNotEmpty, context.RepoRefByType(context.RepoRefTag, context.RepoRefByTypeOptions{IgnoreNotExistErr: true})) | 			repo.MustBeNotEmpty, context.RepoRefByType(context.RepoRefTag, context.RepoRefByTypeOptions{IgnoreNotExistErr: true})) | ||||||
| 		m.Post("/tags/delete", repo.DeleteTag, reqSignIn, | 		m.Post("/tags/delete", repo.DeleteTag, reqSignIn, | ||||||
| 			repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoCodeWriter, context.RepoRef()) | 			repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoCodeWriter, context.RepoRef()) | ||||||
| 	}, ignSignIn, context.RepoAssignment, reqRepoCodeReader) | 	}, optSignIn, context.RepoAssignment, reqRepoCodeReader) | ||||||
| 	// end "/{username}/{reponame}": repo tags | 	// end "/{username}/{reponame}": repo tags | ||||||
| 
 | 
 | ||||||
| 	m.Group("/{username}/{reponame}", func() { // repo releases | 	m.Group("/{username}/{reponame}", func() { // repo releases | ||||||
| @ -1356,12 +1357,12 @@ func registerRoutes(m *web.Router) { | |||||||
| 			m.Get("/edit/*", repo.EditRelease) | 			m.Get("/edit/*", repo.EditRelease) | ||||||
| 			m.Post("/edit/*", web.Bind(forms.EditReleaseForm{}), repo.EditReleasePost) | 			m.Post("/edit/*", web.Bind(forms.EditReleaseForm{}), repo.EditReleasePost) | ||||||
| 		}, reqSignIn, repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, repo.CommitInfoCache) | 		}, reqSignIn, repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, repo.CommitInfoCache) | ||||||
| 	}, ignSignIn, context.RepoAssignment, reqRepoReleaseReader) | 	}, optSignIn, context.RepoAssignment, reqRepoReleaseReader) | ||||||
| 	// end "/{username}/{reponame}": repo releases | 	// end "/{username}/{reponame}": repo releases | ||||||
| 
 | 
 | ||||||
| 	m.Group("/{username}/{reponame}", func() { // to maintain compatibility with old attachments | 	m.Group("/{username}/{reponame}", func() { // to maintain compatibility with old attachments | ||||||
| 		m.Get("/attachments/{uuid}", repo.GetAttachment) | 		m.Get("/attachments/{uuid}", repo.GetAttachment) | ||||||
| 	}, ignSignIn, context.RepoAssignment) | 	}, optSignIn, context.RepoAssignment) | ||||||
| 	// end "/{username}/{reponame}": compatibility with old attachments | 	// end "/{username}/{reponame}": compatibility with old attachments | ||||||
| 
 | 
 | ||||||
| 	m.Group("/{username}/{reponame}", func() { | 	m.Group("/{username}/{reponame}", func() { | ||||||
| @ -1372,7 +1373,7 @@ func registerRoutes(m *web.Router) { | |||||||
| 		if setting.Packages.Enabled { | 		if setting.Packages.Enabled { | ||||||
| 			m.Get("/packages", repo.Packages) | 			m.Get("/packages", repo.Packages) | ||||||
| 		} | 		} | ||||||
| 	}, ignSignIn, context.RepoAssignment) | 	}, optSignIn, context.RepoAssignment) | ||||||
| 
 | 
 | ||||||
| 	m.Group("/{username}/{reponame}/projects", func() { | 	m.Group("/{username}/{reponame}/projects", func() { | ||||||
| 		m.Get("", repo.Projects) | 		m.Get("", repo.Projects) | ||||||
| @ -1397,7 +1398,7 @@ func registerRoutes(m *web.Router) { | |||||||
| 				}) | 				}) | ||||||
| 			}) | 			}) | ||||||
| 		}, reqRepoProjectsWriter, context.RepoMustNotBeArchived()) | 		}, reqRepoProjectsWriter, context.RepoMustNotBeArchived()) | ||||||
| 	}, ignSignIn, context.RepoAssignment, reqRepoProjectsReader, repo.MustEnableRepoProjects) | 	}, optSignIn, context.RepoAssignment, reqRepoProjectsReader, repo.MustEnableRepoProjects) | ||||||
| 	// end "/{username}/{reponame}/projects" | 	// end "/{username}/{reponame}/projects" | ||||||
| 
 | 
 | ||||||
| 	m.Group("/{username}/{reponame}/actions", func() { | 	m.Group("/{username}/{reponame}/actions", func() { | ||||||
| @ -1427,7 +1428,7 @@ func registerRoutes(m *web.Router) { | |||||||
| 		m.Group("/workflows/{workflow_name}", func() { | 		m.Group("/workflows/{workflow_name}", func() { | ||||||
| 			m.Get("/badge.svg", actions.GetWorkflowBadge) | 			m.Get("/badge.svg", actions.GetWorkflowBadge) | ||||||
| 		}) | 		}) | ||||||
| 	}, ignSignIn, context.RepoAssignment, reqRepoActionsReader, actions.MustEnableActions) | 	}, optSignIn, context.RepoAssignment, reqRepoActionsReader, actions.MustEnableActions) | ||||||
| 	// end "/{username}/{reponame}/actions" | 	// end "/{username}/{reponame}/actions" | ||||||
| 
 | 
 | ||||||
| 	m.Group("/{username}/{reponame}/wiki", func() { | 	m.Group("/{username}/{reponame}/wiki", func() { | ||||||
| @ -1440,7 +1441,7 @@ func registerRoutes(m *web.Router) { | |||||||
| 		m.Get("/commit/{sha:[a-f0-9]{7,64}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff) | 		m.Get("/commit/{sha:[a-f0-9]{7,64}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff) | ||||||
| 		m.Get("/commit/{sha:[a-f0-9]{7,64}}.{ext:patch|diff}", repo.RawDiff) | 		m.Get("/commit/{sha:[a-f0-9]{7,64}}.{ext:patch|diff}", repo.RawDiff) | ||||||
| 		m.Get("/raw/*", repo.WikiRaw) | 		m.Get("/raw/*", repo.WikiRaw) | ||||||
| 	}, ignSignIn, context.RepoAssignment, repo.MustEnableWiki, reqRepoWikiReader, func(ctx *context.Context) { | 	}, optSignIn, context.RepoAssignment, repo.MustEnableWiki, reqRepoWikiReader, func(ctx *context.Context) { | ||||||
| 		ctx.Data["PageIsWiki"] = true | 		ctx.Data["PageIsWiki"] = true | ||||||
| 		ctx.Data["CloneButtonOriginLink"] = ctx.Repo.Repository.WikiCloneLink() | 		ctx.Data["CloneButtonOriginLink"] = ctx.Repo.Repository.WikiCloneLink() | ||||||
| 	}) | 	}) | ||||||
| @ -1462,7 +1463,7 @@ func registerRoutes(m *web.Router) { | |||||||
| 			m.Get("/data", repo.RecentCommitsData) | 			m.Get("/data", repo.RecentCommitsData) | ||||||
| 		}) | 		}) | ||||||
| 	}, | 	}, | ||||||
| 		ignSignIn, context.RepoAssignment, context.RequireRepoReaderOr(unit.TypePullRequests, unit.TypeIssues, unit.TypeReleases), | 		optSignIn, context.RepoAssignment, context.RequireRepoReaderOr(unit.TypePullRequests, unit.TypeIssues, unit.TypeReleases), | ||||||
| 		context.RepoRef(), repo.MustBeNotEmpty, | 		context.RepoRef(), repo.MustBeNotEmpty, | ||||||
| 	) | 	) | ||||||
| 	// end "/{username}/{reponame}/activity" | 	// end "/{username}/{reponame}/activity" | ||||||
| @ -1493,7 +1494,7 @@ func registerRoutes(m *web.Router) { | |||||||
| 				}, context.RepoMustNotBeArchived()) | 				}, context.RepoMustNotBeArchived()) | ||||||
| 			}) | 			}) | ||||||
| 		}) | 		}) | ||||||
| 	}, ignSignIn, context.RepoAssignment, repo.MustAllowPulls, reqRepoPullsReader) | 	}, optSignIn, context.RepoAssignment, repo.MustAllowPulls, reqRepoPullsReader) | ||||||
| 	// end "/{username}/{reponame}/pulls/{index}": repo pull request | 	// end "/{username}/{reponame}/pulls/{index}": repo pull request | ||||||
| 
 | 
 | ||||||
| 	m.Group("/{username}/{reponame}", func() { | 	m.Group("/{username}/{reponame}", func() { | ||||||
| @ -1593,7 +1594,7 @@ func registerRoutes(m *web.Router) { | |||||||
| 		m.Get("/forks", context.RepoRef(), repo.Forks) | 		m.Get("/forks", context.RepoRef(), repo.Forks) | ||||||
| 		m.Get("/commit/{sha:([a-f0-9]{7,64})}.{ext:patch|diff}", repo.MustBeNotEmpty, repo.RawDiff) | 		m.Get("/commit/{sha:([a-f0-9]{7,64})}.{ext:patch|diff}", repo.MustBeNotEmpty, repo.RawDiff) | ||||||
| 		m.Post("/lastcommit/*", context.RepoRefByType(context.RepoRefCommit), repo.LastCommit) | 		m.Post("/lastcommit/*", context.RepoRefByType(context.RepoRefCommit), repo.LastCommit) | ||||||
| 	}, ignSignIn, context.RepoAssignment, reqRepoCodeReader) | 	}, optSignIn, context.RepoAssignment, reqRepoCodeReader) | ||||||
| 	// end "/{username}/{reponame}": repo code | 	// end "/{username}/{reponame}": repo code | ||||||
| 
 | 
 | ||||||
| 	m.Group("/{username}/{reponame}", func() { | 	m.Group("/{username}/{reponame}", func() { | ||||||
| @ -1601,13 +1602,11 @@ func registerRoutes(m *web.Router) { | |||||||
| 		m.Get("/watchers", repo.Watchers) | 		m.Get("/watchers", repo.Watchers) | ||||||
| 		m.Get("/search", reqRepoCodeReader, repo.Search) | 		m.Get("/search", reqRepoCodeReader, repo.Search) | ||||||
| 		m.Post("/action/{action}", reqSignIn, repo.Action) | 		m.Post("/action/{action}", reqSignIn, repo.Action) | ||||||
| 	}, ignSignIn, context.RepoAssignment, context.RepoRef()) | 	}, optSignIn, context.RepoAssignment, context.RepoRef()) | ||||||
| 
 | 
 | ||||||
| 	common.AddOwnerRepoGitLFSRoutes(m, ignSignInAndCsrf, lfsServerEnabled) | 	common.AddOwnerRepoGitLFSRoutes(m, optSignInIgnoreCsrf, lfsServerEnabled) // "/{username}/{reponame}/{lfs-paths}": git-lfs support | ||||||
| 	m.Group("/{username}/{reponame}", func() { | 
 | ||||||
| 		gitHTTPRouters(m) | 	addOwnerRepoGitHTTPRouters(m) // "/{username}/{reponame}/{git-paths}": git http support | ||||||
| 	}) |  | ||||||
| 	// end "/{username}/{reponame}.git": git support |  | ||||||
| 
 | 
 | ||||||
| 	m.Group("/notifications", func() { | 	m.Group("/notifications", func() { | ||||||
| 		m.Get("", user.Notifications) | 		m.Get("", user.Notifications) | ||||||
|  | |||||||
| @ -30,6 +30,10 @@ type contextValuePair struct { | |||||||
| 	valueFn func() any | 	valueFn func() any | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | type BaseContextKeyType struct{} | ||||||
|  | 
 | ||||||
|  | var BaseContextKey BaseContextKeyType | ||||||
|  | 
 | ||||||
| type Base struct { | type Base struct { | ||||||
| 	originCtx     context.Context | 	originCtx     context.Context | ||||||
| 	contextValues []contextValuePair | 	contextValues []contextValuePair | ||||||
| @ -315,6 +319,7 @@ func NewBaseContext(resp http.ResponseWriter, req *http.Request) (b *Base, close | |||||||
| 		Data:      middleware.GetContextData(req.Context()), | 		Data:      middleware.GetContextData(req.Context()), | ||||||
| 	} | 	} | ||||||
| 	b.Req = b.Req.WithContext(b) | 	b.Req = b.Req.WithContext(b) | ||||||
|  | 	b.AppendContextValue(BaseContextKey, b) | ||||||
| 	b.AppendContextValue(translation.ContextKey, b.Locale) | 	b.AppendContextValue(translation.ContextKey, b.Locale) | ||||||
| 	b.AppendContextValue(httplib.RequestContextKey, b.Req) | 	b.AppendContextValue(httplib.RequestContextKey, b.Req) | ||||||
| 	return b, b.cleanUp | 	return b, b.cleanUp | ||||||
|  | |||||||
| @ -65,6 +65,9 @@ type Context struct { | |||||||
| type TemplateContext map[string]any | type TemplateContext map[string]any | ||||||
| 
 | 
 | ||||||
| func init() { | func init() { | ||||||
|  | 	web.RegisterResponseStatusProvider[*Base](func(req *http.Request) web_types.ResponseStatusProvider { | ||||||
|  | 		return req.Context().Value(BaseContextKey).(*Base) | ||||||
|  | 	}) | ||||||
| 	web.RegisterResponseStatusProvider[*Context](func(req *http.Request) web_types.ResponseStatusProvider { | 	web.RegisterResponseStatusProvider[*Context](func(req *http.Request) web_types.ResponseStatusProvider { | ||||||
| 		return req.Context().Value(WebContextKey).(*Context) | 		return req.Context().Value(WebContextKey).(*Context) | ||||||
| 	}) | 	}) | ||||||
|  | |||||||
| @ -51,7 +51,7 @@ func GetListLockHandler(ctx *context.Context) { | |||||||
| 	repository, err := repo_model.GetRepositoryByOwnerAndName(ctx, rv.User, rv.Repo) | 	repository, err := repo_model.GetRepositoryByOwnerAndName(ctx, rv.User, rv.Repo) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Debug("Could not find repository: %s/%s - %s", rv.User, rv.Repo, err) | 		log.Debug("Could not find repository: %s/%s - %s", rv.User, rv.Repo, err) | ||||||
| 		ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs") | 		ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="gitea-lfs"`) | ||||||
| 		ctx.JSON(http.StatusUnauthorized, api.LFSLockError{ | 		ctx.JSON(http.StatusUnauthorized, api.LFSLockError{ | ||||||
| 			Message: "You must have pull access to list locks", | 			Message: "You must have pull access to list locks", | ||||||
| 		}) | 		}) | ||||||
| @ -66,7 +66,7 @@ func GetListLockHandler(ctx *context.Context) { | |||||||
| 
 | 
 | ||||||
| 	authenticated := authenticate(ctx, repository, rv.Authorization, true, false) | 	authenticated := authenticate(ctx, repository, rv.Authorization, true, false) | ||||||
| 	if !authenticated { | 	if !authenticated { | ||||||
| 		ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs") | 		ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="gitea-lfs"`) | ||||||
| 		ctx.JSON(http.StatusUnauthorized, api.LFSLockError{ | 		ctx.JSON(http.StatusUnauthorized, api.LFSLockError{ | ||||||
| 			Message: "You must have pull access to list locks", | 			Message: "You must have pull access to list locks", | ||||||
| 		}) | 		}) | ||||||
| @ -143,7 +143,7 @@ func PostLockHandler(ctx *context.Context) { | |||||||
| 	repository, err := repo_model.GetRepositoryByOwnerAndName(ctx, userName, repoName) | 	repository, err := repo_model.GetRepositoryByOwnerAndName(ctx, userName, repoName) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error("Unable to get repository: %s/%s Error: %v", userName, repoName, err) | 		log.Error("Unable to get repository: %s/%s Error: %v", userName, repoName, err) | ||||||
| 		ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs") | 		ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="gitea-lfs"`) | ||||||
| 		ctx.JSON(http.StatusUnauthorized, api.LFSLockError{ | 		ctx.JSON(http.StatusUnauthorized, api.LFSLockError{ | ||||||
| 			Message: "You must have push access to create locks", | 			Message: "You must have push access to create locks", | ||||||
| 		}) | 		}) | ||||||
| @ -158,7 +158,7 @@ func PostLockHandler(ctx *context.Context) { | |||||||
| 
 | 
 | ||||||
| 	authenticated := authenticate(ctx, repository, authorization, true, true) | 	authenticated := authenticate(ctx, repository, authorization, true, true) | ||||||
| 	if !authenticated { | 	if !authenticated { | ||||||
| 		ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs") | 		ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="gitea-lfs"`) | ||||||
| 		ctx.JSON(http.StatusUnauthorized, api.LFSLockError{ | 		ctx.JSON(http.StatusUnauthorized, api.LFSLockError{ | ||||||
| 			Message: "You must have push access to create locks", | 			Message: "You must have push access to create locks", | ||||||
| 		}) | 		}) | ||||||
| @ -191,7 +191,7 @@ func PostLockHandler(ctx *context.Context) { | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 		if git_model.IsErrLFSUnauthorizedAction(err) { | 		if git_model.IsErrLFSUnauthorizedAction(err) { | ||||||
| 			ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs") | 			ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="gitea-lfs"`) | ||||||
| 			ctx.JSON(http.StatusUnauthorized, api.LFSLockError{ | 			ctx.JSON(http.StatusUnauthorized, api.LFSLockError{ | ||||||
| 				Message: "You must have push access to create locks : " + err.Error(), | 				Message: "You must have push access to create locks : " + err.Error(), | ||||||
| 			}) | 			}) | ||||||
| @ -215,7 +215,7 @@ func VerifyLockHandler(ctx *context.Context) { | |||||||
| 	repository, err := repo_model.GetRepositoryByOwnerAndName(ctx, userName, repoName) | 	repository, err := repo_model.GetRepositoryByOwnerAndName(ctx, userName, repoName) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error("Unable to get repository: %s/%s Error: %v", userName, repoName, err) | 		log.Error("Unable to get repository: %s/%s Error: %v", userName, repoName, err) | ||||||
| 		ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs") | 		ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="gitea-lfs"`) | ||||||
| 		ctx.JSON(http.StatusUnauthorized, api.LFSLockError{ | 		ctx.JSON(http.StatusUnauthorized, api.LFSLockError{ | ||||||
| 			Message: "You must have push access to verify locks", | 			Message: "You must have push access to verify locks", | ||||||
| 		}) | 		}) | ||||||
| @ -230,7 +230,7 @@ func VerifyLockHandler(ctx *context.Context) { | |||||||
| 
 | 
 | ||||||
| 	authenticated := authenticate(ctx, repository, authorization, true, true) | 	authenticated := authenticate(ctx, repository, authorization, true, true) | ||||||
| 	if !authenticated { | 	if !authenticated { | ||||||
| 		ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs") | 		ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="gitea-lfs"`) | ||||||
| 		ctx.JSON(http.StatusUnauthorized, api.LFSLockError{ | 		ctx.JSON(http.StatusUnauthorized, api.LFSLockError{ | ||||||
| 			Message: "You must have push access to verify locks", | 			Message: "You must have push access to verify locks", | ||||||
| 		}) | 		}) | ||||||
| @ -286,7 +286,7 @@ func UnLockHandler(ctx *context.Context) { | |||||||
| 	repository, err := repo_model.GetRepositoryByOwnerAndName(ctx, userName, repoName) | 	repository, err := repo_model.GetRepositoryByOwnerAndName(ctx, userName, repoName) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error("Unable to get repository: %s/%s Error: %v", userName, repoName, err) | 		log.Error("Unable to get repository: %s/%s Error: %v", userName, repoName, err) | ||||||
| 		ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs") | 		ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="gitea-lfs"`) | ||||||
| 		ctx.JSON(http.StatusUnauthorized, api.LFSLockError{ | 		ctx.JSON(http.StatusUnauthorized, api.LFSLockError{ | ||||||
| 			Message: "You must have push access to delete locks", | 			Message: "You must have push access to delete locks", | ||||||
| 		}) | 		}) | ||||||
| @ -301,7 +301,7 @@ func UnLockHandler(ctx *context.Context) { | |||||||
| 
 | 
 | ||||||
| 	authenticated := authenticate(ctx, repository, authorization, true, true) | 	authenticated := authenticate(ctx, repository, authorization, true, true) | ||||||
| 	if !authenticated { | 	if !authenticated { | ||||||
| 		ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs") | 		ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="gitea-lfs"`) | ||||||
| 		ctx.JSON(http.StatusUnauthorized, api.LFSLockError{ | 		ctx.JSON(http.StatusUnauthorized, api.LFSLockError{ | ||||||
| 			Message: "You must have push access to delete locks", | 			Message: "You must have push access to delete locks", | ||||||
| 		}) | 		}) | ||||||
| @ -324,7 +324,7 @@ func UnLockHandler(ctx *context.Context) { | |||||||
| 	lock, err := git_model.DeleteLFSLockByID(ctx, ctx.PathParamInt64("lid"), repository, ctx.Doer, req.Force) | 	lock, err := git_model.DeleteLFSLockByID(ctx, ctx.PathParamInt64("lid"), repository, ctx.Doer, req.Force) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if git_model.IsErrLFSUnauthorizedAction(err) { | 		if git_model.IsErrLFSUnauthorizedAction(err) { | ||||||
| 			ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs") | 			ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="gitea-lfs"`) | ||||||
| 			ctx.JSON(http.StatusUnauthorized, api.LFSLockError{ | 			ctx.JSON(http.StatusUnauthorized, api.LFSLockError{ | ||||||
| 				Message: "You must have push access to delete locks : " + err.Error(), | 				Message: "You must have push access to delete locks : " + err.Error(), | ||||||
| 			}) | 			}) | ||||||
|  | |||||||
| @ -21,7 +21,7 @@ import ( | |||||||
| 	actions_model "code.gitea.io/gitea/models/actions" | 	actions_model "code.gitea.io/gitea/models/actions" | ||||||
| 	auth_model "code.gitea.io/gitea/models/auth" | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	git_model "code.gitea.io/gitea/models/git" | 	git_model "code.gitea.io/gitea/models/git" | ||||||
| 	"code.gitea.io/gitea/models/perm" | 	perm_model "code.gitea.io/gitea/models/perm" | ||||||
| 	access_model "code.gitea.io/gitea/models/perm/access" | 	access_model "code.gitea.io/gitea/models/perm/access" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unit" | 	"code.gitea.io/gitea/models/unit" | ||||||
| @ -77,7 +77,7 @@ func CheckAcceptMediaType(ctx *context.Context) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var rangeHeaderRegexp = regexp.MustCompile(`bytes=(\d+)\-(\d*).*`) | var rangeHeaderRegexp = regexp.MustCompile(`bytes=(\d+)-(\d*).*`) | ||||||
| 
 | 
 | ||||||
| // DownloadHandler gets the content from the content store | // DownloadHandler gets the content from the content store | ||||||
| func DownloadHandler(ctx *context.Context) { | func DownloadHandler(ctx *context.Context) { | ||||||
| @ -507,11 +507,11 @@ func writeStatusMessage(ctx *context.Context, status int, message string) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // authenticate uses the authorization string to determine whether | // authenticate uses the authorization string to determine whether | ||||||
| // or not to proceed. This server assumes an HTTP Basic auth format. | // to proceed. This server assumes an HTTP Basic auth format. | ||||||
| func authenticate(ctx *context.Context, repository *repo_model.Repository, authorization string, requireSigned, requireWrite bool) bool { | func authenticate(ctx *context.Context, repository *repo_model.Repository, authorization string, requireSigned, requireWrite bool) bool { | ||||||
| 	accessMode := perm.AccessModeRead | 	accessMode := perm_model.AccessModeRead | ||||||
| 	if requireWrite { | 	if requireWrite { | ||||||
| 		accessMode = perm.AccessModeWrite | 		accessMode = perm_model.AccessModeWrite | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if ctx.Data["IsActionsToken"] == true { | 	if ctx.Data["IsActionsToken"] == true { | ||||||
| @ -526,9 +526,9 @@ func authenticate(ctx *context.Context, repository *repo_model.Repository, autho | |||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if task.IsForkPullRequest { | 		if task.IsForkPullRequest { | ||||||
| 			return accessMode <= perm.AccessModeRead | 			return accessMode <= perm_model.AccessModeRead | ||||||
| 		} | 		} | ||||||
| 		return accessMode <= perm.AccessModeWrite | 		return accessMode <= perm_model.AccessModeWrite | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// ctx.IsSigned is unnecessary here, this will be checked in perm.CanAccess | 	// ctx.IsSigned is unnecessary here, this will be checked in perm.CanAccess | ||||||
| @ -553,7 +553,7 @@ func authenticate(ctx *context.Context, repository *repo_model.Repository, autho | |||||||
| 	return true | 	return true | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func handleLFSToken(ctx stdCtx.Context, tokenSHA string, target *repo_model.Repository, mode perm.AccessMode) (*user_model.User, error) { | func handleLFSToken(ctx stdCtx.Context, tokenSHA string, target *repo_model.Repository, mode perm_model.AccessMode) (*user_model.User, error) { | ||||||
| 	if !strings.Contains(tokenSHA, ".") { | 	if !strings.Contains(tokenSHA, ".") { | ||||||
| 		return nil, nil | 		return nil, nil | ||||||
| 	} | 	} | ||||||
| @ -576,7 +576,7 @@ func handleLFSToken(ctx stdCtx.Context, tokenSHA string, target *repo_model.Repo | |||||||
| 		return nil, fmt.Errorf("invalid token claim") | 		return nil, fmt.Errorf("invalid token claim") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if mode == perm.AccessModeWrite && claims.Op != "upload" { | 	if mode == perm_model.AccessModeWrite && claims.Op != "upload" { | ||||||
| 		return nil, fmt.Errorf("invalid token claim") | 		return nil, fmt.Errorf("invalid token claim") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -588,7 +588,7 @@ func handleLFSToken(ctx stdCtx.Context, tokenSHA string, target *repo_model.Repo | |||||||
| 	return u, nil | 	return u, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func parseToken(ctx stdCtx.Context, authorization string, target *repo_model.Repository, mode perm.AccessMode) (*user_model.User, error) { | func parseToken(ctx stdCtx.Context, authorization string, target *repo_model.Repository, mode perm_model.AccessMode) (*user_model.User, error) { | ||||||
| 	if authorization == "" { | 	if authorization == "" { | ||||||
| 		return nil, fmt.Errorf("no token") | 		return nil, fmt.Errorf("no token") | ||||||
| 	} | 	} | ||||||
| @ -608,6 +608,6 @@ func parseToken(ctx stdCtx.Context, authorization string, target *repo_model.Rep | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func requireAuth(ctx *context.Context) { | func requireAuth(ctx *context.Context) { | ||||||
| 	ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs") | 	ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="gitea-lfs"`) | ||||||
| 	writeStatus(ctx, http.StatusUnauthorized) | 	writeStatus(ctx, http.StatusUnauthorized) | ||||||
| } | } | ||||||
|  | |||||||
| @ -4,14 +4,18 @@ | |||||||
| package integration | package integration | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	gocontext "context" | ||||||
| 	"net/url" | 	"net/url" | ||||||
|  | 	"slices" | ||||||
|  | 	"strings" | ||||||
| 	"sync" | 	"sync" | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| 	auth_model "code.gitea.io/gitea/models/auth" | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
|  | 	"code.gitea.io/gitea/modules/git" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/modules/web" | 	"code.gitea.io/gitea/modules/web" | ||||||
| 	"code.gitea.io/gitea/routers/private" | 	"code.gitea.io/gitea/routers/common" | ||||||
| 	"code.gitea.io/gitea/services/context" | 	"code.gitea.io/gitea/services/context" | ||||||
| 
 | 
 | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| @ -25,7 +29,7 @@ func TestGitLFSSSH(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| 		var mu sync.Mutex | 		var mu sync.Mutex | ||||||
| 		var routerCalls []string | 		var routerCalls []string | ||||||
| 		web.RouteMock(private.RouterMockPointInternalLFS, func(ctx *context.PrivateContext) { | 		web.RouteMock(common.RouterMockPointCommonLFS, func(ctx *context.Base) { | ||||||
| 			mu.Lock() | 			mu.Lock() | ||||||
| 			routerCalls = append(routerCalls, ctx.Req.Method+" "+ctx.Req.URL.Path) | 			routerCalls = append(routerCalls, ctx.Req.Method+" "+ctx.Req.URL.Path) | ||||||
| 			mu.Unlock() | 			mu.Unlock() | ||||||
| @ -42,20 +46,18 @@ func TestGitLFSSSH(t *testing.T) { | |||||||
| 			setting.LFS.AllowPureSSH = true | 			setting.LFS.AllowPureSSH = true | ||||||
| 			require.NoError(t, cfg.Save()) | 			require.NoError(t, cfg.Save()) | ||||||
| 
 | 
 | ||||||
| 			// do LFS SSH transfer? | 			_, _, cmdErr := git.NewCommand(gocontext.Background(), "config", "lfs.sshtransfer", "always").RunStdString(&git.RunOpts{Dir: dstPath}) | ||||||
|  | 			assert.NoError(t, cmdErr) | ||||||
| 			lfsCommitAndPushTest(t, dstPath, 10) | 			lfsCommitAndPushTest(t, dstPath, 10) | ||||||
| 		}) | 		}) | ||||||
| 
 | 
 | ||||||
| 		// FIXME: Here we only see the following calls, but actually there should be calls to "PUT"? | 		countBatch := slices.ContainsFunc(routerCalls, func(s string) bool { | ||||||
| 		// 0 = {string} "GET /api/internal/repo/user2/repo1.git/info/lfs/locks" | 			return strings.Contains(s, "POST /api/internal/repo/user2/repo1.git/info/lfs/objects/batch") | ||||||
| 		// 1 = {string} "POST /api/internal/repo/user2/repo1.git/info/lfs/objects/batch" | 		}) | ||||||
| 		// 2 = {string} "GET /api/internal/repo/user2/repo1.git/info/lfs/locks" | 		countUpload := slices.ContainsFunc(routerCalls, func(s string) bool { | ||||||
| 		// 3 = {string} "POST /api/internal/repo/user2/repo1.git/info/lfs/locks" | 			return strings.Contains(s, "PUT /user2/repo1.git/info/lfs/objects/") | ||||||
| 		// 4 = {string} "GET /api/internal/repo/user2/repo1.git/info/lfs/locks" | 		}) | ||||||
| 		// 5 = {string} "GET /api/internal/repo/user2/repo1.git/info/lfs/locks" | 		assert.NotZero(t, countBatch) | ||||||
| 		// 6 = {string} "GET /api/internal/repo/user2/repo1.git/info/lfs/locks" | 		assert.NotZero(t, countUpload) | ||||||
| 		// 7 = {string} "POST /api/internal/repo/user2/repo1.git/info/lfs/locks/24/unlock" |  | ||||||
| 		assert.NotEmpty(t, routerCalls) |  | ||||||
| 		// assert.Contains(t, routerCalls, "PUT /api/internal/repo/user2/repo1.git/info/lfs/objects/....") |  | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user