forked from gitea/gitea
		
	Move code from module to service (#24287)
The code should not be in `modules/` but `services/`. Reference: https://github.com/go-gitea/gitea/pull/24257#discussion_r1174578230
This commit is contained in:
		
							parent
							
								
									b2248d2553
								
							
						
					
					
						commit
						55a600fa41
					
				| @ -1,194 +0,0 @@ | ||||
| // Copyright 2014 The Gogs Authors. All rights reserved. | ||||
| // Copyright 2019 The Gitea Authors. All rights reserved. | ||||
| // SPDX-License-Identifier: MIT | ||||
| 
 | ||||
| package context | ||||
| 
 | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/models/auth" | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/web/middleware" | ||||
| ) | ||||
| 
 | ||||
| // ToggleOptions contains required or check options | ||||
| type ToggleOptions struct { | ||||
| 	SignInRequired  bool | ||||
| 	SignOutRequired bool | ||||
| 	AdminRequired   bool | ||||
| 	DisableCSRF     bool | ||||
| } | ||||
| 
 | ||||
| // Toggle returns toggle options as middleware | ||||
| func Toggle(options *ToggleOptions) func(ctx *Context) { | ||||
| 	return func(ctx *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 if user tries to visit any non-login page. | ||||
| 		if options.SignOutRequired && ctx.IsSigned && ctx.Req.URL.RequestURI() != "/" { | ||||
| 			ctx.Redirect(setting.AppSubURL + "/") | ||||
| 			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 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // ToggleAPI returns toggle options as middleware | ||||
| func ToggleAPI(options *ToggleOptions) func(ctx *APIContext) { | ||||
| 	return func(ctx *APIContext) { | ||||
| 		// Check prohibit login users. | ||||
| 		if ctx.IsSigned { | ||||
| 			if !ctx.Doer.IsActive && setting.Service.RegisterEmailConfirm { | ||||
| 				ctx.Data["Title"] = ctx.Tr("auth.active_your_account") | ||||
| 				ctx.JSON(http.StatusForbidden, map[string]string{ | ||||
| 					"message": "This account is not activated.", | ||||
| 				}) | ||||
| 				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.JSON(http.StatusForbidden, map[string]string{ | ||||
| 					"message": "This account is prohibited from signing in, please contact your site administrator.", | ||||
| 				}) | ||||
| 				return | ||||
| 			} | ||||
| 
 | ||||
| 			if ctx.Doer.MustChangePassword { | ||||
| 				ctx.JSON(http.StatusForbidden, map[string]string{ | ||||
| 					"message": "You must change your password. Change it at: " + setting.AppURL + "/user/change_password", | ||||
| 				}) | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// Redirect to dashboard if user tries to visit any non-login page. | ||||
| 		if options.SignOutRequired && ctx.IsSigned && ctx.Req.URL.RequestURI() != "/" { | ||||
| 			ctx.Redirect(setting.AppSubURL + "/") | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		if options.SignInRequired { | ||||
| 			if !ctx.IsSigned { | ||||
| 				// Restrict API calls with error message. | ||||
| 				ctx.JSON(http.StatusForbidden, map[string]string{ | ||||
| 					"message": "Only signed in user is allowed to call APIs.", | ||||
| 				}) | ||||
| 				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 | ||||
| 			} | ||||
| 			if ctx.IsSigned && ctx.IsBasicAuth { | ||||
| 				if skip, ok := ctx.Data["SkipLocalTwoFA"]; ok && skip.(bool) { | ||||
| 					return // Skip 2FA | ||||
| 				} | ||||
| 				twofa, err := auth.GetTwoFactorByUID(ctx.Doer.ID) | ||||
| 				if err != nil { | ||||
| 					if auth.IsErrTwoFactorNotEnrolled(err) { | ||||
| 						return // No 2FA enrollment for this user | ||||
| 					} | ||||
| 					ctx.InternalServerError(err) | ||||
| 					return | ||||
| 				} | ||||
| 				otpHeader := ctx.Req.Header.Get("X-Gitea-OTP") | ||||
| 				ok, err := twofa.ValidateTOTP(otpHeader) | ||||
| 				if err != nil { | ||||
| 					ctx.InternalServerError(err) | ||||
| 					return | ||||
| 				} | ||||
| 				if !ok { | ||||
| 					ctx.JSON(http.StatusForbidden, map[string]string{ | ||||
| 						"message": "Only signed in user is allowed to call APIs.", | ||||
| 					}) | ||||
| 					return | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if options.AdminRequired { | ||||
| 			if !ctx.Doer.IsAdmin { | ||||
| 				ctx.JSON(http.StatusForbidden, map[string]string{ | ||||
| 					"message": "You have no permission to request for this.", | ||||
| 				}) | ||||
| 				return | ||||
| 			} | ||||
| 			ctx.Data["PageIsAdmin"] = true | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @ -689,7 +689,7 @@ func Routes(ctx gocontext.Context) *web.Route { | ||||
| 	// Get user from session if logged in. | ||||
| 	m.Use(auth.APIAuth(group)) | ||||
| 
 | ||||
| 	m.Use(context.ToggleAPI(&context.ToggleOptions{ | ||||
| 	m.Use(auth.VerifyAuthWithOptionsAPI(&auth.VerifyOptions{ | ||||
| 		SignInRequired: setting.Service.RequireSignInView, | ||||
| 	})) | ||||
| 
 | ||||
|  | ||||
| @ -228,11 +228,11 @@ func Routes(ctx gocontext.Context) *web.Route { | ||||
| 
 | ||||
| // RegisterRoutes register routes | ||||
| func RegisterRoutes(m *web.Route) { | ||||
| 	reqSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: true}) | ||||
| 	ignSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: setting.Service.RequireSignInView}) | ||||
| 	ignExploreSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: setting.Service.RequireSignInView || setting.Service.Explore.RequireSigninView}) | ||||
| 	ignSignInAndCsrf := context.Toggle(&context.ToggleOptions{DisableCSRF: true}) | ||||
| 	reqSignOut := context.Toggle(&context.ToggleOptions{SignOutRequired: true}) | ||||
| 	reqSignIn := auth_service.VerifyAuthWithOptions(&auth_service.VerifyOptions{SignInRequired: true}) | ||||
| 	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}) | ||||
| 	reqSignOut := auth_service.VerifyAuthWithOptions(&auth_service.VerifyOptions{SignOutRequired: true}) | ||||
| 	validation.AddBindingRules() | ||||
| 
 | ||||
| 	linkAccountEnabled := func(ctx *context.Context) { | ||||
| @ -551,7 +551,7 @@ func RegisterRoutes(m *web.Route) { | ||||
| 
 | ||||
| 	m.Get("/avatar/{hash}", user.AvatarByEmailHash) | ||||
| 
 | ||||
| 	adminReq := context.Toggle(&context.ToggleOptions{SignInRequired: true, AdminRequired: true}) | ||||
| 	adminReq := auth_service.VerifyAuthWithOptions(&auth_service.VerifyOptions{SignInRequired: true, AdminRequired: true}) | ||||
| 
 | ||||
| 	// ***** START: Admin ***** | ||||
| 	m.Group("/admin", func() { | ||||
|  | ||||
| @ -5,9 +5,12 @@ package auth | ||||
| 
 | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/models/auth" | ||||
| 	"code.gitea.io/gitea/modules/context" | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/web/middleware" | ||||
| ) | ||||
| 
 | ||||
| @ -58,3 +61,182 @@ func authShared(ctx *context.Context, authMethod Method) error { | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // VerifyOptions contains required or check options | ||||
| type VerifyOptions struct { | ||||
| 	SignInRequired  bool | ||||
| 	SignOutRequired bool | ||||
| 	AdminRequired   bool | ||||
| 	DisableCSRF     bool | ||||
| } | ||||
| 
 | ||||
| // Checks authentication according to options | ||||
| func VerifyAuthWithOptions(options *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 if user tries to visit any non-login page. | ||||
| 		if options.SignOutRequired && ctx.IsSigned && ctx.Req.URL.RequestURI() != "/" { | ||||
| 			ctx.Redirect(setting.AppSubURL + "/") | ||||
| 			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 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Checks authentication according to options | ||||
| func VerifyAuthWithOptionsAPI(options *VerifyOptions) func(ctx *context.APIContext) { | ||||
| 	return func(ctx *context.APIContext) { | ||||
| 		// Check prohibit login users. | ||||
| 		if ctx.IsSigned { | ||||
| 			if !ctx.Doer.IsActive && setting.Service.RegisterEmailConfirm { | ||||
| 				ctx.Data["Title"] = ctx.Tr("auth.active_your_account") | ||||
| 				ctx.JSON(http.StatusForbidden, map[string]string{ | ||||
| 					"message": "This account is not activated.", | ||||
| 				}) | ||||
| 				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.JSON(http.StatusForbidden, map[string]string{ | ||||
| 					"message": "This account is prohibited from signing in, please contact your site administrator.", | ||||
| 				}) | ||||
| 				return | ||||
| 			} | ||||
| 
 | ||||
| 			if ctx.Doer.MustChangePassword { | ||||
| 				ctx.JSON(http.StatusForbidden, map[string]string{ | ||||
| 					"message": "You must change your password. Change it at: " + setting.AppURL + "/user/change_password", | ||||
| 				}) | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// Redirect to dashboard if user tries to visit any non-login page. | ||||
| 		if options.SignOutRequired && ctx.IsSigned && ctx.Req.URL.RequestURI() != "/" { | ||||
| 			ctx.Redirect(setting.AppSubURL + "/") | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		if options.SignInRequired { | ||||
| 			if !ctx.IsSigned { | ||||
| 				// Restrict API calls with error message. | ||||
| 				ctx.JSON(http.StatusForbidden, map[string]string{ | ||||
| 					"message": "Only signed in user is allowed to call APIs.", | ||||
| 				}) | ||||
| 				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 | ||||
| 			} | ||||
| 			if ctx.IsSigned && ctx.IsBasicAuth { | ||||
| 				if skip, ok := ctx.Data["SkipLocalTwoFA"]; ok && skip.(bool) { | ||||
| 					return // Skip 2FA | ||||
| 				} | ||||
| 				twofa, err := auth.GetTwoFactorByUID(ctx.Doer.ID) | ||||
| 				if err != nil { | ||||
| 					if auth.IsErrTwoFactorNotEnrolled(err) { | ||||
| 						return // No 2FA enrollment for this user | ||||
| 					} | ||||
| 					ctx.InternalServerError(err) | ||||
| 					return | ||||
| 				} | ||||
| 				otpHeader := ctx.Req.Header.Get("X-Gitea-OTP") | ||||
| 				ok, err := twofa.ValidateTOTP(otpHeader) | ||||
| 				if err != nil { | ||||
| 					ctx.InternalServerError(err) | ||||
| 					return | ||||
| 				} | ||||
| 				if !ok { | ||||
| 					ctx.JSON(http.StatusForbidden, map[string]string{ | ||||
| 						"message": "Only signed in user is allowed to call APIs.", | ||||
| 					}) | ||||
| 					return | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if options.AdminRequired { | ||||
| 			if !ctx.Doer.IsAdmin { | ||||
| 				ctx.JSON(http.StatusForbidden, map[string]string{ | ||||
| 					"message": "You have no permission to request for this.", | ||||
| 				}) | ||||
| 				return | ||||
| 			} | ||||
| 			ctx.Data["PageIsAdmin"] = true | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 KN4CK3R
						KN4CK3R