From 9482b023a815e54c79ee9994fe07d4c30d49bcff Mon Sep 17 00:00:00 2001
From: wxiaoguang <wxiaoguang@gmail.com>
Date: Tue, 5 Mar 2024 10:12:03 +0800
Subject: [PATCH] Make "/user/login" page redirect if the current user has
 signed in (#29583)

Fix #29582 and maybe more.
Maybe fix #29116

(cherry picked from commit df1268ca08aaacae54c775a8eec34006dfe365e0)
---
 routers/web/auth/auth.go              | 30 ++++++++++++-------
 routers/web/auth/auth_test.go         | 43 +++++++++++++++++++++++++++
 routers/web/repo/wiki_test.go         |  2 +-
 services/contexttest/context_tests.go |  3 +-
 4 files changed, 66 insertions(+), 12 deletions(-)
 create mode 100644 routers/web/auth/auth_test.go

diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go
index 1adb891840..1c55256db4 100644
--- a/routers/web/auth/auth.go
+++ b/routers/web/auth/auth.go
@@ -135,9 +135,21 @@ func resetLocale(ctx *context.Context, u *user_model.User) error {
 	return nil
 }
 
+func RedirectAfterLogin(ctx *context.Context) {
+	redirectTo := ctx.FormString("redirect_to")
+	if redirectTo == "" {
+		redirectTo = ctx.GetSiteCookie("redirect_to")
+	}
+	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
+	}
+	ctx.RedirectToFirst(redirectTo, nextRedirectTo)
+}
+
 func CheckAutoLogin(ctx *context.Context) bool {
-	// Check auto-login
-	isSucceed, err := autoSignIn(ctx)
+	isSucceed, err := autoSignIn(ctx) // try to auto-login
 	if err != nil {
 		ctx.ServerError("autoSignIn", err)
 		return true
@@ -146,17 +158,10 @@ func CheckAutoLogin(ctx *context.Context) bool {
 	redirectTo := ctx.FormString("redirect_to")
 	if len(redirectTo) > 0 {
 		middleware.SetRedirectToCookie(ctx.Resp, redirectTo)
-	} else {
-		redirectTo = ctx.GetSiteCookie("redirect_to")
 	}
 
 	if isSucceed {
-		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
-		}
-		ctx.RedirectToFirst(redirectTo, nextRedirectTo)
+		RedirectAfterLogin(ctx)
 		return true
 	}
 
@@ -171,6 +176,11 @@ func SignIn(ctx *context.Context) {
 		return
 	}
 
+	if ctx.IsSigned {
+		RedirectAfterLogin(ctx)
+		return
+	}
+
 	oauth2Providers, err := oauth2.GetOAuth2Providers(ctx, optional.Some(true))
 	if err != nil {
 		ctx.ServerError("UserSignIn", err)
diff --git a/routers/web/auth/auth_test.go b/routers/web/auth/auth_test.go
new file mode 100644
index 0000000000..c6afbf877c
--- /dev/null
+++ b/routers/web/auth/auth_test.go
@@ -0,0 +1,43 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package auth
+
+import (
+	"net/http"
+	"net/url"
+	"testing"
+
+	"code.gitea.io/gitea/modules/test"
+	"code.gitea.io/gitea/services/contexttest"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestUserLogin(t *testing.T) {
+	ctx, resp := contexttest.MockContext(t, "/user/login")
+	SignIn(ctx)
+	assert.Equal(t, http.StatusOK, resp.Code)
+
+	ctx, resp = contexttest.MockContext(t, "/user/login")
+	ctx.IsSigned = true
+	SignIn(ctx)
+	assert.Equal(t, http.StatusSeeOther, resp.Code)
+	assert.Equal(t, "/", test.RedirectURL(resp))
+
+	ctx, resp = contexttest.MockContext(t, "/user/login?redirect_to=/other")
+	ctx.IsSigned = true
+	SignIn(ctx)
+	assert.Equal(t, "/other", test.RedirectURL(resp))
+
+	ctx, resp = contexttest.MockContext(t, "/user/login")
+	ctx.Req.AddCookie(&http.Cookie{Name: "redirect_to", Value: "/other-cookie"})
+	ctx.IsSigned = true
+	SignIn(ctx)
+	assert.Equal(t, "/other-cookie", test.RedirectURL(resp))
+
+	ctx, resp = contexttest.MockContext(t, "/user/login?redirect_to="+url.QueryEscape("https://example.com"))
+	ctx.IsSigned = true
+	SignIn(ctx)
+	assert.Equal(t, "/", test.RedirectURL(resp))
+}
diff --git a/routers/web/repo/wiki_test.go b/routers/web/repo/wiki_test.go
index 49c83cfef5..719cca3049 100644
--- a/routers/web/repo/wiki_test.go
+++ b/routers/web/repo/wiki_test.go
@@ -79,7 +79,7 @@ func assertPagesMetas(t *testing.T, expectedNames []string, metas any) {
 func TestWiki(t *testing.T) {
 	unittest.PrepareTestEnv(t)
 
-	ctx, _ := contexttest.MockContext(t, "user2/repo1/wiki/?action=_pages")
+	ctx, _ := contexttest.MockContext(t, "user2/repo1/wiki")
 	ctx.SetParams("*", "Home")
 	contexttest.LoadRepo(t, ctx, 1)
 	Wiki(ctx)
diff --git a/services/contexttest/context_tests.go b/services/contexttest/context_tests.go
index 431017a30d..d3e6de7efe 100644
--- a/services/contexttest/context_tests.go
+++ b/services/contexttest/context_tests.go
@@ -7,6 +7,7 @@ package contexttest
 import (
 	gocontext "context"
 	"io"
+	"maps"
 	"net/http"
 	"net/http/httptest"
 	"net/url"
@@ -36,7 +37,7 @@ func mockRequest(t *testing.T, reqPath string) *http.Request {
 	}
 	requestURL, err := url.Parse(path)
 	assert.NoError(t, err)
-	req := &http.Request{Method: method, URL: requestURL, Form: url.Values{}}
+	req := &http.Request{Method: method, URL: requestURL, Form: maps.Clone(requestURL.Query()), Header: http.Header{}}
 	req = req.WithContext(middleware.WithContextData(req.Context()))
 	return req
 }