diff --git a/modules/markup/html_link.go b/modules/markup/html_link.go
index 7523ebaed0..1702950da8 100644
--- a/modules/markup/html_link.go
+++ b/modules/markup/html_link.go
@@ -208,7 +208,6 @@ func createDescriptionLink(href, content string) *html.Node {
Attr: []html.Attribute{
{Key: "href", Val: href},
{Key: "target", Val: "_blank"},
- {Key: "rel", Val: "noopener noreferrer"},
},
}
textNode.Parent = linkNode
diff --git a/modules/markup/sanitizer_description_test.go b/modules/markup/sanitizer_description_test.go
index ca72491f26..51833414f4 100644
--- a/modules/markup/sanitizer_description_test.go
+++ b/modules/markup/sanitizer_description_test.go
@@ -16,7 +16,7 @@ func TestDescriptionSanitizer(t *testing.T) {
`THUMBS UP`, `THUMBS UP`,
`Hello World`, `Hello World`,
`
`, ``,
- `https://example.com`, `https://example.com`,
+ `https://example.com`, `https://example.com`,
`data`, `data`,
`Important!`, `Important!`,
`Click me! Nothing to see here.
`, `Click me! Nothing to see here.`,
diff --git a/modules/web/middleware/cookie.go b/modules/web/middleware/cookie.go
index ad9aee6478..f98aceba10 100644
--- a/modules/web/middleware/cookie.go
+++ b/modules/web/middleware/cookie.go
@@ -14,14 +14,24 @@ import (
"code.gitea.io/gitea/modules/util"
)
+const cookieRedirectTo = "redirect_to"
+
+func GetRedirectToCookie(req *http.Request) string {
+ return GetSiteCookie(req, cookieRedirectTo)
+}
+
// SetRedirectToCookie convenience function to set the RedirectTo cookie consistently
func SetRedirectToCookie(resp http.ResponseWriter, value string) {
- SetSiteCookie(resp, "redirect_to", value, 0)
+ SetSiteCookie(resp, cookieRedirectTo, value, 0)
}
// DeleteRedirectToCookie convenience function to delete most cookies consistently
func DeleteRedirectToCookie(resp http.ResponseWriter) {
- SetSiteCookie(resp, "redirect_to", "", -1)
+ SetSiteCookie(resp, cookieRedirectTo, "", -1)
+}
+
+func RedirectLinkUserLogin(req *http.Request) string {
+ return setting.AppSubURL + "/user/login?redirect_to=" + url.QueryEscape(setting.AppSubURL+req.URL.RequestURI())
}
// GetSiteCookie returns given cookie value from request header.
diff --git a/routers/common/blockexpensive.go b/routers/common/blockexpensive.go
index ebec0a2e5b..fec364351c 100644
--- a/routers/common/blockexpensive.go
+++ b/routers/common/blockexpensive.go
@@ -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
}
}
diff --git a/routers/web/auth/2fa.go b/routers/web/auth/2fa.go
index 1f087a7897..a19c9d7aca 100644
--- a/routers/web/auth/2fa.go
+++ b/routers/web/auth/2fa.go
@@ -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
}
diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go
index d36fb5bab7..bc0939d92a 100644
--- a/routers/web/auth/auth.go
+++ b/routers/web/auth/auth.go
@@ -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
diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go
index 5eab7ffeb4..b96ea17bc3 100644
--- a/routers/web/auth/oauth.go
+++ b/routers/web/auth/oauth.go
@@ -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
}
diff --git a/routers/web/auth/openid.go b/routers/web/auth/openid.go
index 4ef4c96ccc..948e65366e 100644
--- a/routers/web/auth/openid.go
+++ b/routers/web/auth/openid.go
@@ -35,7 +35,7 @@ func SignInOpenID(ctx *context.Context) {
return
}
- if CheckAutoLogin(ctx) {
+ if performAutoLogin(ctx) {
return
}
diff --git a/routers/web/auth/password.go b/routers/web/auth/password.go
index 537ad4b994..61c6119470 100644
--- a/routers/web/auth/password.go
+++ b/routers/web/auth/password.go
@@ -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)
}
diff --git a/routers/web/auth/webauthn.go b/routers/web/auth/webauthn.go
index dacb6be225..cae726b8bf 100644
--- a/routers/web/auth/webauthn.go
+++ b/routers/web/auth/webauthn.go
@@ -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))
}
diff --git a/routers/web/repo/issue_view.go b/routers/web/repo/issue_view.go
index 803afbffe4..a97f32e018 100644
--- a/routers/web/repo/issue_view.go
+++ b/routers/web/repo/issue_view.go
@@ -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)
diff --git a/routers/web/web.go b/routers/web/web.go
index 4da8cdb581..64137876e0 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -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
}
diff --git a/services/context/base.go b/services/context/base.go
index 8bd66bed09..4baea95ccf 100644
--- a/services/context/base.go
+++ b/services/context/base.go
@@ -18,6 +18,7 @@ import (
"code.gitea.io/gitea/modules/reqctx"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/translation"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web/middleware"
)
@@ -147,10 +148,7 @@ func (b *Base) PlainText(status int, text string) {
// Redirect redirects the request
func (b *Base) Redirect(location string, status ...int) {
- code := http.StatusSeeOther
- if len(status) == 1 {
- code = status[0]
- }
+ code := util.OptionalArg(status, http.StatusSeeOther)
if !httplib.IsRelativeURL(location) {
// Some browsers (Safari) have buggy behavior for Cookie + Cache + External Redirection, eg: /my-path => https://other/path
diff --git a/services/context/context.go b/services/context/context.go
index 420b2aefa8..e12a97eeef 100644
--- a/services/context/context.go
+++ b/services/context/context.go
@@ -145,7 +145,6 @@ func Contexter() func(next http.Handler) http.Handler {
base := NewBaseContext(resp, req)
ctx := NewWebContext(base, rnd, session.GetContextSession(req))
ctx.Data.MergeFrom(middleware.CommonTemplateContextData())
- ctx.Data["CurrentURL"] = setting.AppSubURL + req.URL.RequestURI()
ctx.Data["Link"] = ctx.Link
// PageData is passed by reference, and it will be rendered to `window.config.pageData` in `head.tmpl` for JavaScript modules
diff --git a/templates/base/footer_content.tmpl b/templates/base/footer_content.tmpl
index df437badf6..66c9d718ea 100644
--- a/templates/base/footer_content.tmpl
+++ b/templates/base/footer_content.tmpl
@@ -1,7 +1,7 @@