diff options
author | KN4CK3R <admin@oldschoolhack.me> | 2023-09-12 08:15:16 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-09-12 08:15:16 +0200 |
commit | 591f586bf1c40114451284318a8c4f37e196a51a (patch) | |
tree | 552d65be95fc980632319b30b0a34e0df0a92236 /routers/web/web.go | |
parent | 7818121d50fd9ce3739c3d2221402841781e04b9 (diff) | |
download | gitea-591f586bf1c40114451284318a8c4f37e196a51a.tar.gz gitea-591f586bf1c40114451284318a8c4f37e196a51a.zip |
Extract auth middleware from service (#27028)
Related #27027
Extract the router logic from `services/auth/middleware.go` into
`routers/web` <-> `routers/common` <-> `routers/api`.
Diffstat (limited to 'routers/web/web.go')
-rw-r--r-- | routers/web/web.go | 123 |
1 files changed, 114 insertions, 9 deletions
diff --git a/routers/web/web.go b/routers/web/web.go index ec6742f6ce..077a076864 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -6,6 +6,7 @@ package web import ( gocontext "context" "net/http" + "strings" "code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/unit" @@ -19,6 +20,7 @@ import ( "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/validation" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/modules/web/middleware" "code.gitea.io/gitea/modules/web/routing" "code.gitea.io/gitea/routers/common" "code.gitea.io/gitea/routers/web/admin" @@ -46,7 +48,7 @@ import ( "gitea.com/go-chi/captcha" "github.com/NYTimes/gziphandler" - "github.com/go-chi/chi/v5/middleware" + chi_middleware "github.com/go-chi/chi/v5/middleware" "github.com/go-chi/cors" "github.com/prometheus/client_golang/prometheus" ) @@ -95,6 +97,109 @@ func buildAuthGroup() *auth_service.Group { return group } +func webAuth(authMethod auth_service.Method) func(*context.Context) { + return func(ctx *context.Context) { + ar, err := common.AuthShared(ctx.Base, ctx.Session, authMethod) + if err != nil { + log.Error("Failed to verify user: %v", err) + ctx.Error(http.StatusUnauthorized, "Verify") + return + } + ctx.Doer = ar.Doer + ctx.IsSigned = ar.Doer != nil + ctx.IsBasicAuth = ar.IsBasicAuth + if ctx.Doer == nil { + // ensure the session uid is deleted + _ = ctx.Session.Delete("uid") + } + } +} + +// verifyAuthWithOptions checks authentication according to options +func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.Context) { + return func(ctx *context.Context) { + // Check prohibit login users. + if ctx.IsSigned { + if !ctx.Doer.IsActive && setting.Service.RegisterEmailConfirm { + ctx.Data["Title"] = ctx.Tr("auth.active_your_account") + ctx.HTML(http.StatusOK, "user/auth/activate") + return + } + if !ctx.Doer.IsActive || ctx.Doer.ProhibitLogin { + log.Info("Failed authentication attempt for %s from %s", ctx.Doer.Name, ctx.RemoteAddr()) + ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") + ctx.HTML(http.StatusOK, "user/auth/prohibit_login") + return + } + + if ctx.Doer.MustChangePassword { + if ctx.Req.URL.Path != "/user/settings/change_password" { + if strings.HasPrefix(ctx.Req.UserAgent(), "git") { + ctx.Error(http.StatusUnauthorized, ctx.Tr("auth.must_change_password")) + return + } + ctx.Data["Title"] = ctx.Tr("auth.must_change_password") + ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/change_password" + if ctx.Req.URL.Path != "/user/events" { + middleware.SetRedirectToCookie(ctx.Resp, setting.AppSubURL+ctx.Req.URL.RequestURI()) + } + ctx.Redirect(setting.AppSubURL + "/user/settings/change_password") + return + } + } else if ctx.Req.URL.Path == "/user/settings/change_password" { + // make sure that the form cannot be accessed by users who don't need this + ctx.Redirect(setting.AppSubURL + "/") + return + } + } + + // Redirect to dashboard (or alternate location) if user tries to visit any non-login page. + if options.SignOutRequired && ctx.IsSigned && ctx.Req.URL.RequestURI() != "/" { + ctx.RedirectToFirst(ctx.FormString("redirect_to")) + return + } + + if !options.SignOutRequired && !options.DisableCSRF && ctx.Req.Method == "POST" { + ctx.Csrf.Validate(ctx) + if ctx.Written() { + return + } + } + + if options.SignInRequired { + if !ctx.IsSigned { + if ctx.Req.URL.Path != "/user/events" { + middleware.SetRedirectToCookie(ctx.Resp, setting.AppSubURL+ctx.Req.URL.RequestURI()) + } + ctx.Redirect(setting.AppSubURL + "/user/login") + return + } else if !ctx.Doer.IsActive && setting.Service.RegisterEmailConfirm { + ctx.Data["Title"] = ctx.Tr("auth.active_your_account") + ctx.HTML(http.StatusOK, "user/auth/activate") + return + } + } + + // Redirect to log in page if auto-signin info is provided and has not signed in. + if !options.SignOutRequired && !ctx.IsSigned && + len(ctx.GetSiteCookie(setting.CookieUserName)) > 0 { + if ctx.Req.URL.Path != "/user/events" { + middleware.SetRedirectToCookie(ctx.Resp, setting.AppSubURL+ctx.Req.URL.RequestURI()) + } + ctx.Redirect(setting.AppSubURL + "/user/login") + return + } + + if options.AdminRequired { + if !ctx.Doer.IsAdmin { + ctx.Error(http.StatusForbidden) + return + } + ctx.Data["PageIsAdmin"] = true + } + } +} + func ctxDataSet(args ...any) func(ctx *context.Context) { return func(ctx *context.Context) { for i := 0; i < len(args); i += 2 { @@ -144,10 +249,10 @@ func Routes() *web.Route { mid = append(mid, common.Sessioner(), context.Contexter()) // Get user from session if logged in. - mid = append(mid, auth_service.Auth(buildAuthGroup())) + mid = append(mid, webAuth(buildAuthGroup())) // GetHead allows a HEAD request redirect to GET if HEAD method is not defined for that route - mid = append(mid, middleware.GetHead) + mid = append(mid, chi_middleware.GetHead) if setting.API.EnableSwagger { // Note: The route is here but no in API routes because it renders a web page @@ -168,12 +273,12 @@ func Routes() *web.Route { // registerRoutes register routes func registerRoutes(m *web.Route) { - reqSignIn := auth_service.VerifyAuthWithOptions(&auth_service.VerifyOptions{SignInRequired: true}) - reqSignOut := auth_service.VerifyAuthWithOptions(&auth_service.VerifyOptions{SignOutRequired: true}) + reqSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: 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) - ignSignIn := auth_service.VerifyAuthWithOptions(&auth_service.VerifyOptions{SignInRequired: setting.Service.RequireSignInView}) - ignExploreSignIn := auth_service.VerifyAuthWithOptions(&auth_service.VerifyOptions{SignInRequired: setting.Service.RequireSignInView || setting.Service.Explore.RequireSigninView}) - ignSignInAndCsrf := auth_service.VerifyAuthWithOptions(&auth_service.VerifyOptions{DisableCSRF: true}) + ignSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: setting.Service.RequireSignInView}) + ignExploreSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: setting.Service.RequireSignInView || setting.Service.Explore.RequireSigninView}) + ignSignInAndCsrf := verifyAuthWithOptions(&common.VerifyOptions{DisableCSRF: true}) validation.AddBindingRules() linkAccountEnabled := func(ctx *context.Context) { @@ -543,7 +648,7 @@ func registerRoutes(m *web.Route) { m.Get("/avatar/{hash}", user.AvatarByEmailHash) - adminReq := auth_service.VerifyAuthWithOptions(&auth_service.VerifyOptions{SignInRequired: true, AdminRequired: true}) + adminReq := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: true, AdminRequired: true}) // ***** START: Admin ***** m.Group("/admin", func() { |