diff options
author | wxiaoguang <wxiaoguang@gmail.com> | 2024-12-02 02:03:15 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-12-02 02:03:15 +0800 |
commit | def13ece7c7994e014e34cdc2fc4887d1998f019 (patch) | |
tree | 126317c5640d65467c7298505a7874b7131dab2a | |
parent | 1bb1a51f477e5b17d36aebd4f8bcdc4bbfbb4f74 (diff) | |
download | gitea-def13ece7c7994e014e34cdc2fc4887d1998f019.tar.gz gitea-def13ece7c7994e014e34cdc2fc4887d1998f019.zip |
Allow to disable the password-based login (sign-in) form (#32687)
Usually enterprise/organization users would like to only allow OAuth2
login.
This PR adds a new config option to disable the password-based login
form. It is a simple and clear approach and won't block the future
login-system refactoring works.
Fix a TODO in #24821
Replace #21851
Close #7633 , close #13606
-rw-r--r-- | custom/conf/app.example.ini | 22 | ||||
-rw-r--r-- | modules/setting/service.go | 2 | ||||
-rw-r--r-- | routers/web/auth/auth.go | 48 | ||||
-rw-r--r-- | templates/user/auth/oauth_container.tmpl | 5 | ||||
-rw-r--r-- | templates/user/auth/signin_inner.tmpl | 10 | ||||
-rw-r--r-- | templates/user/auth/signup_inner.tmpl | 5 | ||||
-rw-r--r-- | tests/integration/signin_test.go | 29 |
7 files changed, 73 insertions, 48 deletions
diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index c3b78e60bb..5c23f70d7c 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -784,6 +784,10 @@ LEVEL = Info ;; Please note that setting this to false will not disable OAuth Basic or Basic authentication using a token ;ENABLE_BASIC_AUTHENTICATION = true ;; +;; Show the password sign-in form (for password-based login), otherwise, only show OAuth2 login methods. +;; If you set it to false, maybe it also needs to set ENABLE_BASIC_AUTHENTICATION to false to completely disable password-based authentication. +;ENABLE_PASSWORD_SIGNIN_FORM = true +;; ;; More detail: https://github.com/gogits/gogs/issues/165 ;ENABLE_REVERSE_PROXY_AUTHENTICATION = false ; Enable this to allow reverse proxy authentication for API requests, the reverse proxy is responsible for ensuring that no CSRF is possible. @@ -1944,13 +1948,13 @@ LEVEL = Info ;; Minio secretAccessKey to connect only available when STORAGE_TYPE is `minio` ;MINIO_SECRET_ACCESS_KEY = ;; -;; Preferred IAM Endpoint to override Minio's default IAM Endpoint resolution only available when STORAGE_TYPE is `minio`. -;; If not provided and STORAGE_TYPE is `minio`, will search for and derive endpoint from known environment variables -;; (AWS_CONTAINER_AUTHORIZATION_TOKEN, AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE, AWS_CONTAINER_CREDENTIALS_RELATIVE_URI, -;; AWS_CONTAINER_CREDENTIALS_FULL_URI, AWS_WEB_IDENTITY_TOKEN_FILE, AWS_ROLE_ARN, AWS_ROLE_SESSION_NAME, AWS_REGION), +;; Preferred IAM Endpoint to override Minio's default IAM Endpoint resolution only available when STORAGE_TYPE is `minio`. +;; If not provided and STORAGE_TYPE is `minio`, will search for and derive endpoint from known environment variables +;; (AWS_CONTAINER_AUTHORIZATION_TOKEN, AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE, AWS_CONTAINER_CREDENTIALS_RELATIVE_URI, +;; AWS_CONTAINER_CREDENTIALS_FULL_URI, AWS_WEB_IDENTITY_TOKEN_FILE, AWS_ROLE_ARN, AWS_ROLE_SESSION_NAME, AWS_REGION), ;; or the DefaultIAMRoleEndpoint if not provided otherwise. ;MINIO_IAM_ENDPOINT = -;; +;; ;; Minio bucket to store the attachments only available when STORAGE_TYPE is `minio` ;MINIO_BUCKET = gitea ;; @@ -2695,10 +2699,10 @@ LEVEL = Info ;; Minio secretAccessKey to connect only available when STORAGE_TYPE is `minio` ;MINIO_SECRET_ACCESS_KEY = ;; -;; Preferred IAM Endpoint to override Minio's default IAM Endpoint resolution only available when STORAGE_TYPE is `minio`. -;; If not provided and STORAGE_TYPE is `minio`, will search for and derive endpoint from known environment variables -;; (AWS_CONTAINER_AUTHORIZATION_TOKEN, AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE, AWS_CONTAINER_CREDENTIALS_RELATIVE_URI, -;; AWS_CONTAINER_CREDENTIALS_FULL_URI, AWS_WEB_IDENTITY_TOKEN_FILE, AWS_ROLE_ARN, AWS_ROLE_SESSION_NAME, AWS_REGION), +;; Preferred IAM Endpoint to override Minio's default IAM Endpoint resolution only available when STORAGE_TYPE is `minio`. +;; If not provided and STORAGE_TYPE is `minio`, will search for and derive endpoint from known environment variables +;; (AWS_CONTAINER_AUTHORIZATION_TOKEN, AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE, AWS_CONTAINER_CREDENTIALS_RELATIVE_URI, +;; AWS_CONTAINER_CREDENTIALS_FULL_URI, AWS_WEB_IDENTITY_TOKEN_FILE, AWS_ROLE_ARN, AWS_ROLE_SESSION_NAME, AWS_REGION), ;; or the DefaultIAMRoleEndpoint if not provided otherwise. ;MINIO_IAM_ENDPOINT = ;; diff --git a/modules/setting/service.go b/modules/setting/service.go index c858f80354..526ad64eb4 100644 --- a/modules/setting/service.go +++ b/modules/setting/service.go @@ -41,6 +41,7 @@ var Service = struct { AllowOnlyInternalRegistration bool AllowOnlyExternalRegistration bool ShowRegistrationButton bool + EnablePasswordSignInForm bool ShowMilestonesDashboardPage bool RequireSignInView bool EnableNotifyMail bool @@ -159,6 +160,7 @@ func loadServiceFrom(rootCfg ConfigProvider) { Service.ShowMilestonesDashboardPage = sec.Key("SHOW_MILESTONES_DASHBOARD_PAGE").MustBool(true) Service.RequireSignInView = sec.Key("REQUIRE_SIGNIN_VIEW").MustBool() Service.EnableBasicAuth = sec.Key("ENABLE_BASIC_AUTHENTICATION").MustBool(true) + Service.EnablePasswordSignInForm = sec.Key("ENABLE_PASSWORD_SIGNIN_FORM").MustBool(true) Service.EnableReverseProxyAuth = sec.Key("ENABLE_REVERSE_PROXY_AUTHENTICATION").MustBool() Service.EnableReverseProxyAuthAPI = sec.Key("ENABLE_REVERSE_PROXY_AUTHENTICATION_API").MustBool() Service.EnableReverseProxyAutoRegister = sec.Key("ENABLE_REVERSE_PROXY_AUTO_REGISTRATION").MustBool() diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go index c9ef9193f1..3f16da3cdd 100644 --- a/routers/web/auth/auth.go +++ b/routers/web/auth/auth.go @@ -160,54 +160,42 @@ func CheckAutoLogin(ctx *context.Context) bool { return false } -// SignIn render sign in page -func SignIn(ctx *context.Context) { +func prepareSignInPageData(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("sign_in") - - if CheckAutoLogin(ctx) { - return - } - - if ctx.IsSigned { - RedirectAfterLogin(ctx) - return - } - - oauth2Providers, err := oauth2.GetOAuth2Providers(ctx, optional.Some(true)) - if err != nil { - ctx.ServerError("UserSignIn", err) - return - } - ctx.Data["OAuth2Providers"] = oauth2Providers + ctx.Data["OAuth2Providers"], _ = oauth2.GetOAuth2Providers(ctx, optional.Some(true)) ctx.Data["Title"] = ctx.Tr("sign_in") ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login" ctx.Data["PageIsSignIn"] = true ctx.Data["PageIsLogin"] = true ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled(ctx) + ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm if setting.Service.EnableCaptcha && setting.Service.RequireCaptchaForLogin { context.SetCaptchaData(ctx) } +} +// SignIn render sign in page +func SignIn(ctx *context.Context) { + if CheckAutoLogin(ctx) { + return + } + if ctx.IsSigned { + RedirectAfterLogin(ctx) + return + } + prepareSignInPageData(ctx) ctx.HTML(http.StatusOK, tplSignIn) } // SignInPost response for sign in request func SignInPost(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("sign_in") - - oauth2Providers, err := oauth2.GetOAuth2Providers(ctx, optional.Some(true)) - if err != nil { - ctx.ServerError("UserSignIn", err) + if !setting.Service.EnablePasswordSignInForm { + ctx.Error(http.StatusForbidden) return } - ctx.Data["OAuth2Providers"] = oauth2Providers - ctx.Data["Title"] = ctx.Tr("sign_in") - ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login" - ctx.Data["PageIsSignIn"] = true - ctx.Data["PageIsLogin"] = true - ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled(ctx) + prepareSignInPageData(ctx) if ctx.HasError() { ctx.HTML(http.StatusOK, tplSignIn) return @@ -216,8 +204,6 @@ func SignInPost(ctx *context.Context) { form := web.GetForm(ctx).(*forms.SignInForm) if setting.Service.EnableCaptcha && setting.Service.RequireCaptchaForLogin { - context.SetCaptchaData(ctx) - context.VerifyCaptcha(ctx, tplSignIn, form) if ctx.Written() { return diff --git a/templates/user/auth/oauth_container.tmpl b/templates/user/auth/oauth_container.tmpl index 7599b49fbb..d01aaefe1a 100644 --- a/templates/user/auth/oauth_container.tmpl +++ b/templates/user/auth/oauth_container.tmpl @@ -1,7 +1,3 @@ -{{if or .OAuth2Providers .EnableOpenIDSignIn}} -<div class="divider divider-text"> - {{ctx.Locale.Tr "sign_in_or"}} -</div> <div id="oauth2-login-navigator" class="tw-py-1"> <div class="tw-flex tw-flex-col tw-justify-center"> <div id="oauth2-login-navigator-inner" class="tw-flex tw-flex-col tw-flex-wrap tw-items-center tw-gap-2"> @@ -26,4 +22,3 @@ </div> </div> </div> -{{end}} diff --git a/templates/user/auth/signin_inner.tmpl b/templates/user/auth/signin_inner.tmpl index ec61e56f4d..e0a19a9743 100644 --- a/templates/user/auth/signin_inner.tmpl +++ b/templates/user/auth/signin_inner.tmpl @@ -10,6 +10,7 @@ {{end}} </h4> <div class="ui attached segment"> + {{if .EnablePasswordSignInForm}} <form class="ui form" action="{{.SignInLink}}" method="post"> {{.CsrfTokenHtml}} <div class="required field {{if and (.Err_UserName) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn))}}error{{end}}"> @@ -46,8 +47,13 @@ </button> </div> </form> - - {{template "user/auth/oauth_container" .}} + {{end}}{{/*if .EnablePasswordSignInForm*/}} + {{if and .OAuth2Providers .EnableOpenIDSignIn .EnablePasswordSignInForm}} + <div class="divider divider-text">{{ctx.Locale.Tr "sign_in_or"}}</div> + {{end}} + {{if and .OAuth2Providers .EnableOpenIDSignIn}} + {{template "user/auth/oauth_container" .}} + {{end}} </div> </div> diff --git a/templates/user/auth/signup_inner.tmpl b/templates/user/auth/signup_inner.tmpl index 08507e545d..6969003968 100644 --- a/templates/user/auth/signup_inner.tmpl +++ b/templates/user/auth/signup_inner.tmpl @@ -48,7 +48,10 @@ </div> {{end}} - {{template "user/auth/oauth_container" .}} + {{if and .OAuth2Providers .EnableOpenIDSignIn}} + <div class="divider divider-text">{{ctx.Locale.Tr "sign_in_or"}}</div> + {{template "user/auth/oauth_container" .}} + {{end}} </form> </div> </div> diff --git a/tests/integration/signin_test.go b/tests/integration/signin_test.go index 886d4a8259..abad9eb5e5 100644 --- a/tests/integration/signin_test.go +++ b/tests/integration/signin_test.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/tests" @@ -91,3 +92,31 @@ func TestSigninWithRememberMe(t *testing.T) { req = NewRequest(t, "GET", "/user/settings") session.MakeRequest(t, req, http.StatusOK) } + +func TestEnablePasswordSignInForm(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + t.Run("EnablePasswordSignInForm=false", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + defer test.MockVariableValue(&setting.Service.EnablePasswordSignInForm, false)() + + req := NewRequest(t, "GET", "/user/login") + resp := MakeRequest(t, req, http.StatusOK) + NewHTMLParser(t, resp.Body).AssertElement(t, "form[action='/user/login']", false) + + req = NewRequest(t, "POST", "/user/login") + MakeRequest(t, req, http.StatusForbidden) + }) + + t.Run("EnablePasswordSignInForm=true", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + defer test.MockVariableValue(&setting.Service.EnablePasswordSignInForm, true)() + + req := NewRequest(t, "GET", "/user/login") + resp := MakeRequest(t, req, http.StatusOK) + NewHTMLParser(t, resp.Body).AssertElement(t, "form[action='/user/login']", true) + + req = NewRequest(t, "POST", "/user/login") + MakeRequest(t, req, http.StatusOK) + }) +} |