From d312c9cc0fe5794cfa68d768570a1f84e82346ed Mon Sep 17 00:00:00 2001 From: Damien Goutte-Gattat Date: Fri, 12 Feb 2021 23:28:39 +0000 Subject: [PATCH 1/5] Allow blacklisting of email domains. Add a new configuration setting EMAIL_DOMAIN_BLACKLIST, which accepts a comma-separated lsit of email domains that should not be allowed to register an account on the instance. Signed-off-by: Damien Goutte-Gattat --- modules/forms/user_form.go | 24 ++++++++++++++++++++++++ modules/setting/service.go | 2 ++ routers/user/auth.go | 2 +- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/modules/forms/user_form.go b/modules/forms/user_form.go index af36628c30fda..b003609edde25 100644 --- a/modules/forms/user_form.go +++ b/modules/forms/user_form.go @@ -120,6 +120,30 @@ func (f RegisterForm) IsEmailDomainWhitelisted() bool { return false } +// IsEmailDomainBlacklisted validates that the email address +// does not come from a domain that has been blacklisted. +// In the absence of a blacklist, all addresses are accepted. +func (f RegisterForm) IsEmailDomainBlacklisted() bool { + if len(setting.Service.EmailDomainBlacklist) == 0 { + return false + } + + n := strings.LastIndex(f.Email, "@") + if n <= 0 { + return false + } + + domain := strings.ToLower(f.Email[n+1:]) + + for _, v := range setting.Service.EmailDomainBlacklist { + if strings.ToLower(v) == domain { + return true + } + } + + return false +} + // MustChangePasswordForm form for updating your password after account creation // by an admin type MustChangePasswordForm struct { diff --git a/modules/setting/service.go b/modules/setting/service.go index b6611830be191..292e3dfa5a2da 100644 --- a/modules/setting/service.go +++ b/modules/setting/service.go @@ -20,6 +20,7 @@ var Service struct { RegisterEmailConfirm bool RegisterManualConfirm bool EmailDomainWhitelist []string + EmailDomainBlacklist []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.EmailDomainBlacklist = sec.Key("EMAIL_DOMAIN_BLACKLIST").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 bb877767aef8a..92e6d3b23c0fe 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.IsEmailDomainWhitelisted() || form.IsEmailDomainBlacklisted() { ctx.RenderWithErr(ctx.Tr("auth.email_domain_blacklisted"), tplSignUp, &form) return } From 7e03a98c9ecf27ad614071cec97b29acf12a7c81 Mon Sep 17 00:00:00 2001 From: Damien Goutte-Gattat Date: Fri, 12 Feb 2021 23:30:33 +0000 Subject: [PATCH 2/5] Document the new EMAIL_DOMAIN_BLACKLIST setting. Document the EMAIL_DOMAIN_BLACKLIST setting in the configuration cheat sheet and the sample configuration file. Also mention it in the FAQ as one of the ways to mitigate spam accounts. Signed-off-by: Damien Goutte-Gattat --- custom/conf/app.example.ini | 2 ++ docs/content/doc/advanced/config-cheat-sheet.en-us.md | 1 + docs/content/doc/help/faq.en-us.md | 11 ++++++----- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 747173b5ae47e..c13a5829a2168 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_BLACKLIST = ; 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 b65f59ce0c5c2..fe196733e079c 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_BLACKLIST`: **\**: 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 4346c89c38d21..69f934102450e 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 blacklisting 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` and/or `EMAIL_DOMAIN_BLACKLIST` in your app.ini under `[service]` ### Only allow/block certain OpenID providers From 25494c4aa7169ac0fb2e68775ee8a5ec27f65cb6 Mon Sep 17 00:00:00 2001 From: Damien Goutte-Gattat Date: Sat, 13 Feb 2021 15:40:04 +0000 Subject: [PATCH 3/5] Avoid the term "blacklististing". Rename the EMAIL_DOMAIN_BLACKLIST setting to EMAIL_DOMAIN_BLOCKLIST. Change all references in the code and docs accordingly. Signed-off-by: Damien Goutte-Gattat --- custom/conf/app.example.ini | 2 +- .../content/doc/advanced/config-cheat-sheet.en-us.md | 2 +- docs/content/doc/help/faq.en-us.md | 4 ++-- modules/forms/user_form.go | 12 ++++++------ modules/setting/service.go | 4 ++-- routers/user/auth.go | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index c13a5829a2168..c09ab3e5e914f 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -618,7 +618,7 @@ REGISTER_MANUAL_CONFIRM = false ; 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_BLACKLIST = +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 fe196733e079c..1301c08818477 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -466,7 +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_BLACKLIST`: **\**: If non-empty, list of domain names that cannot 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 69f934102450e..b3cf1aa0e81e5 100644 --- a/docs/content/doc/help/faq.en-us.md +++ b/docs/content/doc/help/faq.en-us.md @@ -120,14 +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 whitelisting or blacklisting certain email domains +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/block certain email domains -You can configure `EMAIL_DOMAIN_WHITELIST` and/or `EMAIL_DOMAIN_BLACKLIST` 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 b003609edde25..9f4d8aaabde79 100644 --- a/modules/forms/user_form.go +++ b/modules/forms/user_form.go @@ -120,11 +120,11 @@ func (f RegisterForm) IsEmailDomainWhitelisted() bool { return false } -// IsEmailDomainBlacklisted validates that the email address -// does not come from a domain that has been blacklisted. -// In the absence of a blacklist, all addresses are accepted. -func (f RegisterForm) IsEmailDomainBlacklisted() bool { - if len(setting.Service.EmailDomainBlacklist) == 0 { +// IsEmailDomainBlocklisted validates that the email address +// does not come from a domain that has been blocklisted. +// In the absence of a blocklist, all addresses are accepted. +func (f RegisterForm) IsEmailDomainBlocklisted() bool { + if len(setting.Service.EmailDomainBlocklist) == 0 { return false } @@ -135,7 +135,7 @@ func (f RegisterForm) IsEmailDomainBlacklisted() bool { domain := strings.ToLower(f.Email[n+1:]) - for _, v := range setting.Service.EmailDomainBlacklist { + for _, v := range setting.Service.EmailDomainBlocklist { if strings.ToLower(v) == domain { return true } diff --git a/modules/setting/service.go b/modules/setting/service.go index 292e3dfa5a2da..fc4326fde5543 100644 --- a/modules/setting/service.go +++ b/modules/setting/service.go @@ -20,7 +20,7 @@ var Service struct { RegisterEmailConfirm bool RegisterManualConfirm bool EmailDomainWhitelist []string - EmailDomainBlacklist []string + EmailDomainBlocklist []string DisableRegistration bool AllowOnlyExternalRegistration bool ShowRegistrationButton bool @@ -73,7 +73,7 @@ func newService() { Service.RegisterManualConfirm = false } Service.EmailDomainWhitelist = sec.Key("EMAIL_DOMAIN_WHITELIST").Strings(",") - Service.EmailDomainBlacklist = sec.Key("EMAIL_DOMAIN_BLACKLIST").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 92e6d3b23c0fe..e9a9e1d57801c 100644 --- a/routers/user/auth.go +++ b/routers/user/auth.go @@ -1129,7 +1129,7 @@ func SignUpPost(ctx *context.Context) { } } - if !form.IsEmailDomainWhitelisted() || form.IsEmailDomainBlacklisted() { + if !form.IsEmailDomainWhitelisted() || form.IsEmailDomainBlocklisted() { ctx.RenderWithErr(ctx.Tr("auth.email_domain_blacklisted"), tplSignUp, &form) return } From 8f2092d6779fe9cab162d0c099888fd9558c3d3f Mon Sep 17 00:00:00 2001 From: Damien Goutte-Gattat Date: Sat, 13 Feb 2021 16:51:56 +0000 Subject: [PATCH 4/5] Factorize lookups against email whitelist/blocklist. Avoid code duplication by using a helper function to check whether an email domain is in a given list. Rename IsEmailDomainWhitelisted to IsEmailDomainAllowed, to better reflect the new semantics of that method which is now checking against either a whitelist or a blocklist. Signed-off-by: Damien Goutte-Gattat --- modules/forms/user_form.go | 47 ++++++++++++--------------------- modules/forms/user_form_test.go | 6 ++--- routers/user/auth.go | 2 +- 3 files changed, 21 insertions(+), 34 deletions(-) diff --git a/modules/forms/user_form.go b/modules/forms/user_form.go index 9f4d8aaabde79..07733baebaca1 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,28 +118,17 @@ func (f RegisterForm) IsEmailDomainWhitelisted() bool { return false } -// IsEmailDomainBlocklisted validates that the email address -// does not come from a domain that has been blocklisted. -// In the absence of a blocklist, all addresses are accepted. -func (f RegisterForm) IsEmailDomainBlocklisted() bool { - if len(setting.Service.EmailDomainBlocklist) == 0 { - return false - } - - n := strings.LastIndex(f.Email, "@") - if n <= 0 { - return false - } - - domain := strings.ToLower(f.Email[n+1:]) - - for _, v := range setting.Service.EmailDomainBlocklist { - if strings.ToLower(v) == domain { - return true - } +// 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 false + return IsEmailDomainListed(setting.Service.EmailDomainWhitelist, f.Email) } // MustChangePasswordForm form for updating your password after account creation diff --git a/modules/forms/user_form_test.go b/modules/forms/user_form_test.go index 6e0518789c14e..0d70bc48e183e 100644 --- a/modules/forms/user_form_test.go +++ b/modules/forms/user_form_test.go @@ -19,7 +19,7 @@ func TestRegisterForm_IsDomainWhiteList_Empty(t *testing.T) { form := RegisterForm{} - assert.True(t, form.IsEmailDomainWhitelisted()) + assert.True(t, form.IsEmailDomainAllowed()) } func TestRegisterForm_IsDomainWhiteList_InvalidEmail(t *testing.T) { @@ -37,7 +37,7 @@ 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()) } } @@ -59,6 +59,6 @@ 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()) } } diff --git a/routers/user/auth.go b/routers/user/auth.go index e9a9e1d57801c..de74055d565f5 100644 --- a/routers/user/auth.go +++ b/routers/user/auth.go @@ -1129,7 +1129,7 @@ func SignUpPost(ctx *context.Context) { } } - if !form.IsEmailDomainWhitelisted() || form.IsEmailDomainBlocklisted() { + if !form.IsEmailDomainAllowed() { ctx.RenderWithErr(ctx.Tr("auth.email_domain_blacklisted"), tplSignUp, &form) return } From 1c48d550adc9adbcc8354007e4b729b9d405a3a4 Mon Sep 17 00:00:00 2001 From: Damien Goutte-Gattat Date: Sat, 13 Feb 2021 17:34:56 +0000 Subject: [PATCH 5/5] Update unit tests for the user_form module. Add a test fixture for the new email domain blocklist setting. Signed-off-by: Damien Goutte-Gattat --- modules/forms/user_form_test.go | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/modules/forms/user_form_test.go b/modules/forms/user_form_test.go index 0d70bc48e183e..9f67143d12c81 100644 --- a/modules/forms/user_form_test.go +++ b/modules/forms/user_form_test.go @@ -12,7 +12,7 @@ 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{} @@ -22,7 +22,7 @@ func TestRegisterForm_IsDomainWhiteList_Empty(t *testing.T) { 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"} @@ -41,7 +41,7 @@ func TestRegisterForm_IsDomainWhiteList_InvalidEmail(t *testing.T) { } } -func TestRegisterForm_IsDomainWhiteList_ValidEmail(t *testing.T) { +func TestRegisterForm_IsDomainAllowed_WhitelistedEmail(t *testing.T) { _ = setting.Service setting.Service.EmailDomainWhitelist = []string{"gitea.io"} @@ -62,3 +62,25 @@ func TestRegisterForm_IsDomainWhiteList_ValidEmail(t *testing.T) { 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()) + } +}