mirror of
https://github.com/go-gitea/gitea.git
synced 2026-01-16 17:10:36 +00:00
Fix link/origin referrer and login redirect (#36279)
Fix #35998 1. Fix `<a rel>` : * "_blank" already means "noopener" * "noreferrer" is already provided by page's `<meta name="referrer">` 2. Fix "redirect_to" mechisam * Use "referer" header to determine the redirect link for a successful login 3. Simplify code and merge duplicate logic
This commit is contained in:
@@ -24,7 +24,7 @@ func BlockExpensive() func(next http.Handler) http.Handler {
|
||||
ret := determineRequestPriority(reqctx.FromContext(req.Context()))
|
||||
if !ret.SignedIn {
|
||||
if ret.Expensive || ret.LongPolling {
|
||||
http.Redirect(w, req, setting.AppSubURL+"/user/login", http.StatusSeeOther)
|
||||
http.Redirect(w, req, middleware.RedirectLinkUserLogin(req), http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ var (
|
||||
func TwoFactor(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("twofa")
|
||||
|
||||
if CheckAutoLogin(ctx) {
|
||||
if performAutoLogin(ctx) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ func TwoFactorPost(ctx *context.Context) {
|
||||
func TwoFactorScratch(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("twofa_scratch")
|
||||
|
||||
if CheckAutoLogin(ctx) {
|
||||
if performAutoLogin(ctx) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ func TwoFactorScratchPost(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
handleSignInFull(ctx, u, remember, false)
|
||||
handleSignInFull(ctx, u, remember)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
@@ -126,20 +127,47 @@ func resetLocale(ctx *context.Context, u *user_model.User) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func RedirectAfterLogin(ctx *context.Context) {
|
||||
func rememberAuthRedirectLink(ctx *context.Context) {
|
||||
redirectTo := ctx.FormString("redirect_to")
|
||||
if redirectTo == "" {
|
||||
redirectTo = ctx.GetSiteCookie("redirect_to")
|
||||
if ref, err := url.Parse(ctx.Req.Referer()); err == nil && httplib.IsCurrentGiteaSiteURL(ctx, ctx.Req.Referer()) {
|
||||
// the request paths starting with "/user/" are either:
|
||||
// * auth related pages: don't redirect back to them
|
||||
// * user settings pages: they have "require sign-in" protection already, no "referer redirect" would happen
|
||||
skipRefererRedirect := strings.HasPrefix(ref.Path, setting.AppSubURL+"/user/")
|
||||
if !skipRefererRedirect {
|
||||
redirectTo = ref.RequestURI()
|
||||
}
|
||||
}
|
||||
}
|
||||
middleware.DeleteRedirectToCookie(ctx.Resp)
|
||||
nextRedirectTo := setting.AppSubURL + string(setting.LandingPageURL)
|
||||
if setting.LandingPageURL == setting.LandingPageLogin {
|
||||
nextRedirectTo = setting.AppSubURL + "/" // do not cycle-redirect to the login page
|
||||
if redirectTo != "" {
|
||||
middleware.SetRedirectToCookie(ctx.Resp, redirectTo)
|
||||
}
|
||||
ctx.RedirectToCurrentSite(redirectTo, nextRedirectTo)
|
||||
}
|
||||
|
||||
func CheckAutoLogin(ctx *context.Context) bool {
|
||||
func consumeAuthRedirectLink(ctx *context.Context) string {
|
||||
redirects := []string{ctx.FormString("redirect_to"), middleware.GetRedirectToCookie(ctx.Req)}
|
||||
middleware.DeleteRedirectToCookie(ctx.Resp)
|
||||
if setting.LandingPageURL == setting.LandingPageLogin {
|
||||
redirects = append(redirects, setting.AppSubURL+"/") // do not cycle-redirect to the login page
|
||||
} else {
|
||||
redirects = append(redirects, setting.AppSubURL+string(setting.LandingPageURL))
|
||||
}
|
||||
for _, link := range redirects {
|
||||
if link != "" && httplib.IsCurrentGiteaSiteURL(ctx, link) {
|
||||
return link
|
||||
}
|
||||
}
|
||||
return setting.AppSubURL + "/"
|
||||
}
|
||||
|
||||
func redirectAfterAuth(ctx *context.Context) {
|
||||
ctx.RedirectToCurrentSite(consumeAuthRedirectLink(ctx))
|
||||
}
|
||||
|
||||
func performAutoLogin(ctx *context.Context) bool {
|
||||
rememberAuthRedirectLink(ctx)
|
||||
|
||||
isSucceed, err := autoSignIn(ctx) // try to auto-login
|
||||
if err != nil {
|
||||
if errors.Is(err, auth_service.ErrAuthTokenInvalidHash) {
|
||||
@@ -150,13 +178,8 @@ func CheckAutoLogin(ctx *context.Context) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
redirectTo := ctx.FormString("redirect_to")
|
||||
if len(redirectTo) > 0 {
|
||||
middleware.SetRedirectToCookie(ctx.Resp, redirectTo)
|
||||
}
|
||||
|
||||
if isSucceed {
|
||||
RedirectAfterLogin(ctx)
|
||||
redirectAfterAuth(ctx)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -181,11 +204,11 @@ func prepareSignInPageData(ctx *context.Context) {
|
||||
|
||||
// SignIn render sign in page
|
||||
func SignIn(ctx *context.Context) {
|
||||
if CheckAutoLogin(ctx) {
|
||||
if performAutoLogin(ctx) {
|
||||
return
|
||||
}
|
||||
if ctx.IsSigned {
|
||||
RedirectAfterLogin(ctx)
|
||||
redirectAfterAuth(ctx)
|
||||
return
|
||||
}
|
||||
prepareSignInPageData(ctx)
|
||||
@@ -295,19 +318,19 @@ func SignInPost(ctx *context.Context) {
|
||||
|
||||
// This handles the final part of the sign-in process of the user.
|
||||
func handleSignIn(ctx *context.Context, u *user_model.User, remember bool) {
|
||||
redirect := handleSignInFull(ctx, u, remember, true)
|
||||
handleSignInFull(ctx, u, remember)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
ctx.Redirect(redirect)
|
||||
redirectAfterAuth(ctx)
|
||||
}
|
||||
|
||||
func handleSignInFull(ctx *context.Context, u *user_model.User, remember, obeyRedirect bool) string {
|
||||
func handleSignInFull(ctx *context.Context, u *user_model.User, remember bool) {
|
||||
if remember {
|
||||
nt, token, err := auth_service.CreateAuthTokenForUserID(ctx, u.ID)
|
||||
if err != nil {
|
||||
ctx.ServerError("CreateAuthTokenForUserID", err)
|
||||
return setting.AppSubURL + "/"
|
||||
return
|
||||
}
|
||||
|
||||
ctx.SetSiteCookie(setting.CookieRememberName, nt.ID+":"+token, setting.LogInRememberDays*timeutil.Day)
|
||||
@@ -316,7 +339,7 @@ func handleSignInFull(ctx *context.Context, u *user_model.User, remember, obeyRe
|
||||
userHasTwoFactorAuth, err := auth.HasTwoFactorOrWebAuthn(ctx, u.ID)
|
||||
if err != nil {
|
||||
ctx.ServerError("HasTwoFactorOrWebAuthn", err)
|
||||
return setting.AppSubURL + "/"
|
||||
return
|
||||
}
|
||||
|
||||
if err := updateSession(ctx, []string{
|
||||
@@ -335,7 +358,7 @@ func handleSignInFull(ctx *context.Context, u *user_model.User, remember, obeyRe
|
||||
session.KeyUserHasTwoFactorAuth: userHasTwoFactorAuth,
|
||||
}); err != nil {
|
||||
ctx.ServerError("RegenerateSession", err)
|
||||
return setting.AppSubURL + "/"
|
||||
return
|
||||
}
|
||||
|
||||
// Language setting of the user overwrites the one previously set
|
||||
@@ -346,7 +369,7 @@ func handleSignInFull(ctx *context.Context, u *user_model.User, remember, obeyRe
|
||||
}
|
||||
if err := user_service.UpdateUser(ctx, u, opts); err != nil {
|
||||
ctx.ServerError("UpdateUser Language", fmt.Errorf("Error updating user language [user: %d, locale: %s]", u.ID, ctx.Locale.Language()))
|
||||
return setting.AppSubURL + "/"
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,21 +382,8 @@ func handleSignInFull(ctx *context.Context, u *user_model.User, remember, obeyRe
|
||||
// Register last login
|
||||
if err := user_service.UpdateUser(ctx, u, &user_service.UpdateOptions{SetLastLogin: true}); err != nil {
|
||||
ctx.ServerError("UpdateUser", err)
|
||||
return setting.AppSubURL + "/"
|
||||
return
|
||||
}
|
||||
|
||||
if redirectTo := ctx.GetSiteCookie("redirect_to"); redirectTo != "" && httplib.IsCurrentGiteaSiteURL(ctx, redirectTo) {
|
||||
middleware.DeleteRedirectToCookie(ctx.Resp)
|
||||
if obeyRedirect {
|
||||
ctx.RedirectToCurrentSite(redirectTo)
|
||||
}
|
||||
return redirectTo
|
||||
}
|
||||
|
||||
if obeyRedirect {
|
||||
ctx.Redirect(setting.AppSubURL + "/")
|
||||
}
|
||||
return setting.AppSubURL + "/"
|
||||
}
|
||||
|
||||
// extractUserNameFromOAuth2 tries to extract a normalized username from the given OAuth2 user.
|
||||
@@ -436,10 +446,7 @@ func SignUp(ctx *context.Context) {
|
||||
// Show Disabled Registration message if DisableRegistration or AllowOnlyExternalRegistration options are true
|
||||
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration || setting.Service.AllowOnlyExternalRegistration
|
||||
|
||||
redirectTo := ctx.FormString("redirect_to")
|
||||
if len(redirectTo) > 0 {
|
||||
middleware.SetRedirectToCookie(ctx.Resp, redirectTo)
|
||||
}
|
||||
rememberAuthRedirectLink(ctx)
|
||||
|
||||
ctx.HTML(http.StatusOK, tplSignUp)
|
||||
}
|
||||
@@ -817,13 +824,7 @@ func handleAccountActivation(ctx *context.Context, user *user_model.User) {
|
||||
}
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("auth.account_activated"))
|
||||
if redirectTo := ctx.GetSiteCookie("redirect_to"); len(redirectTo) > 0 {
|
||||
middleware.DeleteRedirectToCookie(ctx.Resp)
|
||||
ctx.RedirectToCurrentSite(redirectTo)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Redirect(setting.AppSubURL + "/")
|
||||
redirectAfterAuth(ctx)
|
||||
}
|
||||
|
||||
// ActivateEmail render the activate email page
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/session"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/web/middleware"
|
||||
source_service "code.gitea.io/gitea/services/auth/source"
|
||||
"code.gitea.io/gitea/services/auth/source/oauth2"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
@@ -42,10 +41,7 @@ func SignInOAuth(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
redirectTo := ctx.FormString("redirect_to")
|
||||
if len(redirectTo) > 0 {
|
||||
middleware.SetRedirectToCookie(ctx.Resp, redirectTo)
|
||||
}
|
||||
rememberAuthRedirectLink(ctx)
|
||||
|
||||
// try to do a direct callback flow, so we don't authenticate the user again but use the valid accesstoken to get the user
|
||||
user, gothUser, err := oAuth2UserLoginCallback(ctx, authSource, ctx.Req, ctx.Resp)
|
||||
@@ -398,13 +394,7 @@ func handleOAuth2SignIn(ctx *context.Context, authSource *auth.Source, u *user_m
|
||||
return
|
||||
}
|
||||
|
||||
if redirectTo := ctx.GetSiteCookie("redirect_to"); len(redirectTo) > 0 {
|
||||
middleware.DeleteRedirectToCookie(ctx.Resp)
|
||||
ctx.RedirectToCurrentSite(redirectTo)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Redirect(setting.AppSubURL + "/")
|
||||
redirectAfterAuth(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ func SignInOpenID(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if CheckAutoLogin(ctx) {
|
||||
if performAutoLogin(ctx) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
"code.gitea.io/gitea/modules/web/middleware"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
"code.gitea.io/gitea/services/forms"
|
||||
"code.gitea.io/gitea/services/mailer"
|
||||
@@ -236,7 +235,7 @@ func ResetPasswdPost(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
handleSignInFull(ctx, u, remember, false)
|
||||
handleSignInFull(ctx, u, remember)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
@@ -308,11 +307,5 @@ func MustChangePasswordPost(ctx *context.Context) {
|
||||
|
||||
log.Trace("User updated password: %s", ctx.Doer.Name)
|
||||
|
||||
if redirectTo := ctx.GetSiteCookie("redirect_to"); redirectTo != "" {
|
||||
middleware.DeleteRedirectToCookie(ctx.Resp)
|
||||
ctx.RedirectToCurrentSite(redirectTo)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Redirect(setting.AppSubURL + "/")
|
||||
redirectAfterAuth(ctx)
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ var tplWebAuthn templates.TplName = "user/auth/webauthn"
|
||||
func WebAuthn(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("twofa")
|
||||
|
||||
if CheckAutoLogin(ctx) {
|
||||
if performAutoLogin(ctx) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -156,12 +156,8 @@ func WebAuthnPasskeyLogin(ctx *context.Context) {
|
||||
}
|
||||
|
||||
remember := false // TODO: implement remember me
|
||||
redirect := handleSignInFull(ctx, user, remember, false)
|
||||
if redirect == "" {
|
||||
redirect = setting.AppSubURL + "/"
|
||||
}
|
||||
|
||||
ctx.JSONRedirect(redirect)
|
||||
handleSignInFull(ctx, user, remember)
|
||||
ctx.JSONRedirect(consumeAuthRedirectLink(ctx))
|
||||
}
|
||||
|
||||
// WebAuthnLoginAssertion submits a WebAuthn challenge to the browser
|
||||
@@ -274,11 +270,7 @@ func WebAuthnLoginAssertionPost(ctx *context.Context) {
|
||||
}
|
||||
|
||||
remember := ctx.Session.Get("twofaRemember").(bool)
|
||||
redirect := handleSignInFull(ctx, user, remember, false)
|
||||
if redirect == "" {
|
||||
redirect = setting.AppSubURL + "/"
|
||||
}
|
||||
handleSignInFull(ctx, user, remember)
|
||||
_ = ctx.Session.Delete("twofaUid")
|
||||
|
||||
ctx.JSONRedirect(redirect)
|
||||
ctx.JSONRedirect(consumeAuthRedirectLink(ctx))
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
@@ -33,6 +32,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
"code.gitea.io/gitea/modules/templates/vars"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/web/middleware"
|
||||
asymkey_service "code.gitea.io/gitea/services/asymkey"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
"code.gitea.io/gitea/services/context/upload"
|
||||
@@ -408,7 +408,7 @@ func ViewIssue(ctx *context.Context) {
|
||||
}
|
||||
|
||||
ctx.Data["Reference"] = issue.Ref
|
||||
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login?redirect_to=" + url.QueryEscape(ctx.Data["Link"].(string))
|
||||
ctx.Data["SignInLink"] = middleware.RedirectLinkUserLogin(ctx.Req)
|
||||
ctx.Data["IsIssuePoster"] = ctx.IsSigned && issue.IsPoster(ctx.Doer.ID)
|
||||
ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)
|
||||
ctx.Data["HasProjectsWritePermission"] = ctx.Repo.CanWrite(unit.TypeProjects)
|
||||
|
||||
@@ -159,9 +159,7 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.Cont
|
||||
}
|
||||
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())
|
||||
}
|
||||
middleware.SetRedirectToCookie(ctx.Resp, setting.AppSubURL+ctx.Req.URL.RequestURI())
|
||||
ctx.Redirect(setting.AppSubURL + "/user/settings/change_password")
|
||||
return
|
||||
}
|
||||
@@ -172,7 +170,7 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.Cont
|
||||
}
|
||||
}
|
||||
|
||||
// Redirect to dashboard (or alternate location) if user tries to visit any non-login page.
|
||||
// When a signed-in user visits a page that requires sign-out (e.g.: "/user/login"), redirect to home (or alternate location)
|
||||
if options.SignOutRequired && ctx.IsSigned && ctx.Req.URL.RequestURI() != "/" {
|
||||
ctx.RedirectToCurrentSite(ctx.FormString("redirect_to"))
|
||||
return
|
||||
@@ -187,10 +185,7 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.Cont
|
||||
|
||||
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")
|
||||
ctx.Redirect(middleware.RedirectLinkUserLogin(ctx.Req))
|
||||
return
|
||||
} else if !ctx.Doer.IsActive && setting.Service.RegisterEmailConfirm {
|
||||
ctx.Data["Title"] = ctx.Tr("auth.active_your_account")
|
||||
@@ -200,12 +195,8 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.Cont
|
||||
}
|
||||
|
||||
// Redirect to log in page if auto-signin info is provided and has not signed in.
|
||||
if !options.SignOutRequired && !ctx.IsSigned &&
|
||||
ctx.GetSiteCookie(setting.CookieRememberName) != "" {
|
||||
if ctx.Req.URL.Path != "/user/events" {
|
||||
middleware.SetRedirectToCookie(ctx.Resp, setting.AppSubURL+ctx.Req.URL.RequestURI())
|
||||
}
|
||||
ctx.Redirect(setting.AppSubURL + "/user/login")
|
||||
if !options.SignOutRequired && !ctx.IsSigned && ctx.GetSiteCookie(setting.CookieRememberName) != "" {
|
||||
ctx.Redirect(middleware.RedirectLinkUserLogin(ctx.Req))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user