diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 276b3cb5e8..a6965c2cf6 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -617,6 +617,8 @@ REGISTER_MANUAL_CONFIRM = false ; List of domain names that are allowed to be used to register on a Gitea instance ; gitea.io,example.com EMAIL_DOMAIN_WHITELIST = +; Comma-separated list of domain names that are not allowed to be used to register on a Gitea instance +EMAIL_DOMAIN_BLOCKLIST = ; Disallow registration, only allow admins to create accounts. DISABLE_REGISTRATION = false ; Allow registration only using third-party services, it works only when DISABLE_REGISTRATION is false diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md index 2c4d01102c..a7f931b047 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -466,6 +466,7 @@ relation to port exhaustion. - `DEFAULT_ALLOW_ONLY_CONTRIBUTORS_TO_TRACK_TIME`: **true**: Only allow users with write permissions to track time. - `EMAIL_DOMAIN_WHITELIST`: **\**: If non-empty, list of domain names that can only be used to register on this instance. +- `EMAIL_DOMAIN_BLOCKLIST`: **\**: If non-empty, list of domain names that cannot be used to register on this instance - `SHOW_REGISTRATION_BUTTON`: **! DISABLE\_REGISTRATION**: Show Registration Button - `SHOW_MILESTONES_DASHBOARD_PAGE`: **true** Enable this to show the milestones dashboard page - a view of all the user's milestones - `AUTO_WATCH_NEW_REPOS`: **true**: Enable this to let all organisation users watch new repos when they are created diff --git a/docs/content/doc/help/faq.en-us.md b/docs/content/doc/help/faq.en-us.md index 4346c89c38..b3cf1aa0e8 100644 --- a/docs/content/doc/help/faq.en-us.md +++ b/docs/content/doc/help/faq.en-us.md @@ -120,13 +120,14 @@ For more information, refer to Gitea's [API docs]({{< relref "doc/developers/api There are multiple things you can combine to prevent spammers. -1. By only whitelisting certain domains with OpenID (see below) -2. Setting `ENABLE_CAPTCHA` to `true` in your `app.ini` and properly configuring `RECAPTCHA_SECRET` and `RECAPTCHA_SITEKEY` -3. Settings `DISABLE_REGISTRATION` to `true` and creating new users via the [CLI]({{< relref "doc/usage/command-line.en-us.md" >}}), [API]({{< relref "doc/developers/api-usage.en-us.md" >}}), or Gitea's Admin UI +1. By whitelisting or blocklisting certain email domains +2. By only whitelisting certain domains with OpenID (see below) +3. Setting `ENABLE_CAPTCHA` to `true` in your `app.ini` and properly configuring `RECAPTCHA_SECRET` and `RECAPTCHA_SITEKEY` +4. Settings `DISABLE_REGISTRATION` to `true` and creating new users via the [CLI]({{< relref "doc/usage/command-line.en-us.md" >}}), [API]({{< relref "doc/developers/api-usage.en-us.md" >}}), or Gitea's Admin UI -### Only allow certain email domains +### Only allow/block certain email domains -You can configure `EMAIL_DOMAIN_WHITELIST` in your app.ini under `[service]` +You can configure `EMAIL_DOMAIN_WHITELIST` or `EMAIL_DOMAIN_BLOCKLIST` in your app.ini under `[service]` ### Only allow/block certain OpenID providers diff --git a/modules/forms/user_form.go b/modules/forms/user_form.go index af36628c30..07733baeba 100644 --- a/modules/forms/user_form.go +++ b/modules/forms/user_form.go @@ -95,23 +95,21 @@ func (f *RegisterForm) Validate(req *http.Request, errs binding.Errors) binding. return middleware.Validate(errs, ctx.Data, f, ctx.Locale) } -// IsEmailDomainWhitelisted validates that the email address -// provided by the user matches what has been configured . -// If the domain whitelist from the config is empty, it marks the -// email as whitelisted -func (f RegisterForm) IsEmailDomainWhitelisted() bool { - if len(setting.Service.EmailDomainWhitelist) == 0 { - return true +// IsEmailDomainListed checks whether the domain of an email address +// matches a list of domains +func IsEmailDomainListed(list []string, email string) bool { + if len(list) == 0 { + return false } - n := strings.LastIndex(f.Email, "@") + n := strings.LastIndex(email, "@") if n <= 0 { return false } - domain := strings.ToLower(f.Email[n+1:]) + domain := strings.ToLower(email[n+1:]) - for _, v := range setting.Service.EmailDomainWhitelist { + for _, v := range list { if strings.ToLower(v) == domain { return true } @@ -120,6 +118,19 @@ func (f RegisterForm) IsEmailDomainWhitelisted() bool { return false } +// IsEmailDomainAllowed validates that the email address +// provided by the user matches what has been configured . +// The email is marked as allowed if it matches any of the +// domains in the whitelist or if it doesn't match any of +// domains in the blocklist, if any such list is not empty. +func (f RegisterForm) IsEmailDomainAllowed() bool { + if len(setting.Service.EmailDomainWhitelist) == 0 { + return !IsEmailDomainListed(setting.Service.EmailDomainBlocklist, f.Email) + } + + return IsEmailDomainListed(setting.Service.EmailDomainWhitelist, f.Email) +} + // MustChangePasswordForm form for updating your password after account creation // by an admin type MustChangePasswordForm struct { diff --git a/modules/forms/user_form_test.go b/modules/forms/user_form_test.go index 6e0518789c..9f67143d12 100644 --- a/modules/forms/user_form_test.go +++ b/modules/forms/user_form_test.go @@ -12,17 +12,17 @@ import ( "github.com/stretchr/testify/assert" ) -func TestRegisterForm_IsDomainWhiteList_Empty(t *testing.T) { +func TestRegisterForm_IsDomainAllowed_Empty(t *testing.T) { _ = setting.Service setting.Service.EmailDomainWhitelist = []string{} form := RegisterForm{} - assert.True(t, form.IsEmailDomainWhitelisted()) + assert.True(t, form.IsEmailDomainAllowed()) } -func TestRegisterForm_IsDomainWhiteList_InvalidEmail(t *testing.T) { +func TestRegisterForm_IsDomainAllowed_InvalidEmail(t *testing.T) { _ = setting.Service setting.Service.EmailDomainWhitelist = []string{"gitea.io"} @@ -37,11 +37,11 @@ func TestRegisterForm_IsDomainWhiteList_InvalidEmail(t *testing.T) { for _, v := range tt { form := RegisterForm{Email: v.email} - assert.False(t, form.IsEmailDomainWhitelisted()) + assert.False(t, form.IsEmailDomainAllowed()) } } -func TestRegisterForm_IsDomainWhiteList_ValidEmail(t *testing.T) { +func TestRegisterForm_IsDomainAllowed_WhitelistedEmail(t *testing.T) { _ = setting.Service setting.Service.EmailDomainWhitelist = []string{"gitea.io"} @@ -59,6 +59,28 @@ func TestRegisterForm_IsDomainWhiteList_ValidEmail(t *testing.T) { for _, v := range tt { form := RegisterForm{Email: v.email} - assert.Equal(t, v.valid, form.IsEmailDomainWhitelisted()) + assert.Equal(t, v.valid, form.IsEmailDomainAllowed()) + } +} + +func TestRegisterForm_IsDomainAllowed_BlocklistedEmail(t *testing.T) { + _ = setting.Service + + setting.Service.EmailDomainWhitelist = []string{} + setting.Service.EmailDomainBlocklist = []string{"gitea.io"} + + tt := []struct { + email string + valid bool + }{ + {"security@gitea.io", false}, + {"security@gitea.example", true}, + {"hdudhdd", true}, + } + + for _, v := range tt { + form := RegisterForm{Email: v.email} + + assert.Equal(t, v.valid, form.IsEmailDomainAllowed()) } } diff --git a/modules/setting/service.go b/modules/setting/service.go index b6611830be..fc4326fde5 100644 --- a/modules/setting/service.go +++ b/modules/setting/service.go @@ -20,6 +20,7 @@ var Service struct { RegisterEmailConfirm bool RegisterManualConfirm bool EmailDomainWhitelist []string + EmailDomainBlocklist []string DisableRegistration bool AllowOnlyExternalRegistration bool ShowRegistrationButton bool @@ -72,6 +73,7 @@ func newService() { Service.RegisterManualConfirm = false } Service.EmailDomainWhitelist = sec.Key("EMAIL_DOMAIN_WHITELIST").Strings(",") + Service.EmailDomainBlocklist = sec.Key("EMAIL_DOMAIN_BLOCKLIST").Strings(",") Service.ShowRegistrationButton = sec.Key("SHOW_REGISTRATION_BUTTON").MustBool(!(Service.DisableRegistration || Service.AllowOnlyExternalRegistration)) Service.ShowMilestonesDashboardPage = sec.Key("SHOW_MILESTONES_DASHBOARD_PAGE").MustBool(true) Service.RequireSignInView = sec.Key("REQUIRE_SIGNIN_VIEW").MustBool() diff --git a/routers/user/auth.go b/routers/user/auth.go index bb877767ae..de74055d56 100644 --- a/routers/user/auth.go +++ b/routers/user/auth.go @@ -1129,7 +1129,7 @@ func SignUpPost(ctx *context.Context) { } } - if !form.IsEmailDomainWhitelisted() { + if !form.IsEmailDomainAllowed() { ctx.RenderWithErr(ctx.Tr("auth.email_domain_blacklisted"), tplSignUp, &form) return }