mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	Support wildcard in email domain allow/block list (#24831)
Replace #20257 (which is stale and incomplete) Close #20255 Major changes: * Deprecate the "WHITELIST", use "ALLOWLIST" * Add wildcard support for EMAIL_DOMAIN_ALLOWLIST/EMAIL_DOMAIN_BLOCKLIST * Update example config file and document * Improve tests
This commit is contained in:
		@@ -700,11 +700,11 @@ LEVEL = Info
 | 
				
			|||||||
;; Whether a new user needs to be confirmed manually after registration. (Requires `REGISTER_EMAIL_CONFIRM` to be disabled.)
 | 
					;; Whether a new user needs to be confirmed manually after registration. (Requires `REGISTER_EMAIL_CONFIRM` to be disabled.)
 | 
				
			||||||
;REGISTER_MANUAL_CONFIRM = false
 | 
					;REGISTER_MANUAL_CONFIRM = false
 | 
				
			||||||
;;
 | 
					;;
 | 
				
			||||||
;; List of domain names that are allowed to be used to register on a Gitea instance
 | 
					;; List of domain names that are allowed to be used to register on a Gitea instance, wildcard is supported
 | 
				
			||||||
;; gitea.io,example.com
 | 
					;; eg: gitea.io,example.com,*.mydomain.com
 | 
				
			||||||
;EMAIL_DOMAIN_WHITELIST =
 | 
					;EMAIL_DOMAIN_ALLOWLIST =
 | 
				
			||||||
;;
 | 
					;;
 | 
				
			||||||
;; Comma-separated list of domain names that are not allowed to be used to register on a Gitea instance
 | 
					;; Comma-separated list of domain names that are not allowed to be used to register on a Gitea instance, wildcard is supported
 | 
				
			||||||
;EMAIL_DOMAIN_BLOCKLIST =
 | 
					;EMAIL_DOMAIN_BLOCKLIST =
 | 
				
			||||||
;;
 | 
					;;
 | 
				
			||||||
;; Disallow registration, only allow admins to create accounts.
 | 
					;; Disallow registration, only allow admins to create accounts.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -651,9 +651,8 @@ And the following unique queues:
 | 
				
			|||||||
- `ENABLE_TIMETRACKING`: **true**: Enable Timetracking feature.
 | 
					- `ENABLE_TIMETRACKING`: **true**: Enable Timetracking feature.
 | 
				
			||||||
- `DEFAULT_ENABLE_TIMETRACKING`: **true**: Allow repositories to use timetracking by default.
 | 
					- `DEFAULT_ENABLE_TIMETRACKING`: **true**: Allow repositories to use timetracking by default.
 | 
				
			||||||
- `DEFAULT_ALLOW_ONLY_CONTRIBUTORS_TO_TRACK_TIME`: **true**: Only allow users with write permissions to track time.
 | 
					- `DEFAULT_ALLOW_ONLY_CONTRIBUTORS_TO_TRACK_TIME`: **true**: Only allow users with write permissions to track time.
 | 
				
			||||||
- `EMAIL_DOMAIN_WHITELIST`: **\<empty\>**: If non-empty, list of domain names that can only be used to register
 | 
					- `EMAIL_DOMAIN_ALLOWLIST`: **\<empty\>**: If non-empty, comma separated list of domain names that can only be used to register on this instance, wildcard is supported.
 | 
				
			||||||
  on this instance.
 | 
					- `EMAIL_DOMAIN_BLOCKLIST`: **\<empty\>**: If non-empty, comma separated list of domain names that cannot be used to register on this instance, wildcard is supported.
 | 
				
			||||||
- `EMAIL_DOMAIN_BLOCKLIST`: **\<empty\>**: 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_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
 | 
					- `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
 | 
					- `AUTO_WATCH_NEW_REPOS`: **true**: Enable this to let all organisation users watch new repos when they are created
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,8 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/structs"
 | 
						"code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/gobwas/glob"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// enumerates all the types of captchas
 | 
					// enumerates all the types of captchas
 | 
				
			||||||
@@ -33,8 +35,8 @@ var Service = struct {
 | 
				
			|||||||
	ResetPwdCodeLives                       int
 | 
						ResetPwdCodeLives                       int
 | 
				
			||||||
	RegisterEmailConfirm                    bool
 | 
						RegisterEmailConfirm                    bool
 | 
				
			||||||
	RegisterManualConfirm                   bool
 | 
						RegisterManualConfirm                   bool
 | 
				
			||||||
	EmailDomainWhitelist                    []string
 | 
						EmailDomainAllowList                    []glob.Glob
 | 
				
			||||||
	EmailDomainBlocklist                    []string
 | 
						EmailDomainBlockList                    []glob.Glob
 | 
				
			||||||
	DisableRegistration                     bool
 | 
						DisableRegistration                     bool
 | 
				
			||||||
	AllowOnlyInternalRegistration           bool
 | 
						AllowOnlyInternalRegistration           bool
 | 
				
			||||||
	AllowOnlyExternalRegistration           bool
 | 
						AllowOnlyExternalRegistration           bool
 | 
				
			||||||
@@ -114,6 +116,20 @@ func (a AllowedVisibility) ToVisibleTypeSlice() (result []structs.VisibleType) {
 | 
				
			|||||||
	return result
 | 
						return result
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func CompileEmailGlobList(sec ConfigSection, keys ...string) (globs []glob.Glob) {
 | 
				
			||||||
 | 
						for _, key := range keys {
 | 
				
			||||||
 | 
							list := sec.Key(key).Strings(",")
 | 
				
			||||||
 | 
							for _, s := range list {
 | 
				
			||||||
 | 
								if g, err := glob.Compile(s); err == nil {
 | 
				
			||||||
 | 
									globs = append(globs, g)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									log.Error("Skip invalid email allow/block list expression %q: %v", s, err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return globs
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func loadServiceFrom(rootCfg ConfigProvider) {
 | 
					func loadServiceFrom(rootCfg ConfigProvider) {
 | 
				
			||||||
	sec := rootCfg.Section("service")
 | 
						sec := rootCfg.Section("service")
 | 
				
			||||||
	Service.ActiveCodeLives = sec.Key("ACTIVE_CODE_LIVE_MINUTES").MustInt(180)
 | 
						Service.ActiveCodeLives = sec.Key("ACTIVE_CODE_LIVE_MINUTES").MustInt(180)
 | 
				
			||||||
@@ -130,8 +146,11 @@ func loadServiceFrom(rootCfg ConfigProvider) {
 | 
				
			|||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		Service.RegisterManualConfirm = false
 | 
							Service.RegisterManualConfirm = false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	Service.EmailDomainWhitelist = sec.Key("EMAIL_DOMAIN_WHITELIST").Strings(",")
 | 
						if sec.HasKey("EMAIL_DOMAIN_WHITELIST") {
 | 
				
			||||||
	Service.EmailDomainBlocklist = sec.Key("EMAIL_DOMAIN_BLOCKLIST").Strings(",")
 | 
							deprecatedSetting(rootCfg, "service", "EMAIL_DOMAIN_WHITELIST", "service", "EMAIL_DOMAIN_ALLOWLIST", "1.21")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						Service.EmailDomainAllowList = CompileEmailGlobList(sec, "EMAIL_DOMAIN_WHITELIST", "EMAIL_DOMAIN_ALLOWLIST")
 | 
				
			||||||
 | 
						Service.EmailDomainBlockList = CompileEmailGlobList(sec, "EMAIL_DOMAIN_BLOCKLIST")
 | 
				
			||||||
	Service.ShowRegistrationButton = sec.Key("SHOW_REGISTRATION_BUTTON").MustBool(!(Service.DisableRegistration || Service.AllowOnlyExternalRegistration))
 | 
						Service.ShowRegistrationButton = sec.Key("SHOW_REGISTRATION_BUTTON").MustBool(!(Service.DisableRegistration || Service.AllowOnlyExternalRegistration))
 | 
				
			||||||
	Service.ShowMilestonesDashboardPage = sec.Key("SHOW_MILESTONES_DASHBOARD_PAGE").MustBool(true)
 | 
						Service.ShowMilestonesDashboardPage = sec.Key("SHOW_MILESTONES_DASHBOARD_PAGE").MustBool(true)
 | 
				
			||||||
	Service.RequireSignInView = sec.Key("REQUIRE_SIGNIN_VIEW").MustBool()
 | 
						Service.RequireSignInView = sec.Key("REQUIRE_SIGNIN_VIEW").MustBool()
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										46
									
								
								modules/setting/service_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								modules/setting/service_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					// Copyright 2023 The Gitea Authors. All rights reserved.
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: MIT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package setting
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/gobwas/glob"
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestLoadServices(t *testing.T) {
 | 
				
			||||||
 | 
						oldService := Service
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							Service = oldService
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cfg, err := NewConfigProviderFromData(`
 | 
				
			||||||
 | 
					[service]
 | 
				
			||||||
 | 
					EMAIL_DOMAIN_WHITELIST = d1, *.w
 | 
				
			||||||
 | 
					EMAIL_DOMAIN_ALLOWLIST = d2, *.a
 | 
				
			||||||
 | 
					EMAIL_DOMAIN_BLOCKLIST = d3, *.b
 | 
				
			||||||
 | 
					`)
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						loadServiceFrom(cfg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						match := func(globs []glob.Glob, s string) bool {
 | 
				
			||||||
 | 
							for _, g := range globs {
 | 
				
			||||||
 | 
								if g.Match(s) {
 | 
				
			||||||
 | 
									return true
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert.True(t, match(Service.EmailDomainAllowList, "d1"))
 | 
				
			||||||
 | 
						assert.True(t, match(Service.EmailDomainAllowList, "foo.w"))
 | 
				
			||||||
 | 
						assert.True(t, match(Service.EmailDomainAllowList, "d2"))
 | 
				
			||||||
 | 
						assert.True(t, match(Service.EmailDomainAllowList, "foo.a"))
 | 
				
			||||||
 | 
						assert.False(t, match(Service.EmailDomainAllowList, "d3"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert.True(t, match(Service.EmailDomainBlockList, "d3"))
 | 
				
			||||||
 | 
						assert.True(t, match(Service.EmailDomainBlockList, "foo.b"))
 | 
				
			||||||
 | 
						assert.False(t, match(Service.EmailDomainBlockList, "d1"))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -16,6 +16,7 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/web/middleware"
 | 
						"code.gitea.io/gitea/modules/web/middleware"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gitea.com/go-chi/binding"
 | 
						"gitea.com/go-chi/binding"
 | 
				
			||||||
 | 
						"github.com/gobwas/glob"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// InstallForm form for installation page
 | 
					// InstallForm form for installation page
 | 
				
			||||||
@@ -105,8 +106,8 @@ func (f *RegisterForm) Validate(req *http.Request, errs binding.Errors) binding.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// IsEmailDomainListed checks whether the domain of an email address
 | 
					// IsEmailDomainListed checks whether the domain of an email address
 | 
				
			||||||
// matches a list of domains
 | 
					// matches a list of domains
 | 
				
			||||||
func IsEmailDomainListed(list []string, email string) bool {
 | 
					func IsEmailDomainListed(globs []glob.Glob, email string) bool {
 | 
				
			||||||
	if len(list) == 0 {
 | 
						if len(globs) == 0 {
 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -117,8 +118,8 @@ func IsEmailDomainListed(list []string, email string) bool {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	domain := strings.ToLower(email[n+1:])
 | 
						domain := strings.ToLower(email[n+1:])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, v := range list {
 | 
						for _, g := range globs {
 | 
				
			||||||
		if strings.ToLower(v) == domain {
 | 
							if g.Match(domain) {
 | 
				
			||||||
			return true
 | 
								return true
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -131,12 +132,12 @@ func IsEmailDomainListed(list []string, email string) bool {
 | 
				
			|||||||
// The email is marked as allowed if it matches any of the
 | 
					// 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 whitelist or if it doesn't match any of
 | 
				
			||||||
// domains in the blocklist, if any such list is not empty.
 | 
					// domains in the blocklist, if any such list is not empty.
 | 
				
			||||||
func (f RegisterForm) IsEmailDomainAllowed() bool {
 | 
					func (f *RegisterForm) IsEmailDomainAllowed() bool {
 | 
				
			||||||
	if len(setting.Service.EmailDomainWhitelist) == 0 {
 | 
						if len(setting.Service.EmailDomainAllowList) == 0 {
 | 
				
			||||||
		return !IsEmailDomainListed(setting.Service.EmailDomainBlocklist, f.Email)
 | 
							return !IsEmailDomainListed(setting.Service.EmailDomainBlockList, f.Email)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return IsEmailDomainListed(setting.Service.EmailDomainWhitelist, f.Email)
 | 
						return IsEmailDomainListed(setting.Service.EmailDomainAllowList, f.Email)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// MustChangePasswordForm form for updating your password after account creation
 | 
					// MustChangePasswordForm form for updating your password after account creation
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,13 +10,17 @@ import (
 | 
				
			|||||||
	auth_model "code.gitea.io/gitea/models/auth"
 | 
						auth_model "code.gitea.io/gitea/models/auth"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/gobwas/glob"
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestRegisterForm_IsDomainAllowed_Empty(t *testing.T) {
 | 
					func TestRegisterForm_IsDomainAllowed_Empty(t *testing.T) {
 | 
				
			||||||
	_ = setting.Service
 | 
						oldService := setting.Service
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							setting.Service = oldService
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	setting.Service.EmailDomainWhitelist = []string{}
 | 
						setting.Service.EmailDomainAllowList = nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	form := RegisterForm{}
 | 
						form := RegisterForm{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -24,15 +28,18 @@ func TestRegisterForm_IsDomainAllowed_Empty(t *testing.T) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestRegisterForm_IsDomainAllowed_InvalidEmail(t *testing.T) {
 | 
					func TestRegisterForm_IsDomainAllowed_InvalidEmail(t *testing.T) {
 | 
				
			||||||
	_ = setting.Service
 | 
						oldService := setting.Service
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							setting.Service = oldService
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	setting.Service.EmailDomainWhitelist = []string{"gitea.io"}
 | 
						setting.Service.EmailDomainAllowList = []glob.Glob{glob.MustCompile("gitea.io")}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tt := []struct {
 | 
						tt := []struct {
 | 
				
			||||||
		email string
 | 
							email string
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{"securitygieqqq"},
 | 
							{"invalid-email"},
 | 
				
			||||||
		{"hdudhdd"},
 | 
							{"gitea.io"},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, v := range tt {
 | 
						for _, v := range tt {
 | 
				
			||||||
@@ -42,10 +49,13 @@ func TestRegisterForm_IsDomainAllowed_InvalidEmail(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestRegisterForm_IsDomainAllowed_WhitelistedEmail(t *testing.T) {
 | 
					func TestRegisterForm_IsDomainAllowed_AllowedEmail(t *testing.T) {
 | 
				
			||||||
	_ = setting.Service
 | 
						oldService := setting.Service
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							setting.Service = oldService
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	setting.Service.EmailDomainWhitelist = []string{"gitea.io"}
 | 
						setting.Service.EmailDomainAllowList = []glob.Glob{glob.MustCompile("gitea.io"), glob.MustCompile("*.allow")}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tt := []struct {
 | 
						tt := []struct {
 | 
				
			||||||
		email string
 | 
							email string
 | 
				
			||||||
@@ -53,8 +63,11 @@ func TestRegisterForm_IsDomainAllowed_WhitelistedEmail(t *testing.T) {
 | 
				
			|||||||
	}{
 | 
						}{
 | 
				
			||||||
		{"security@gitea.io", true},
 | 
							{"security@gitea.io", true},
 | 
				
			||||||
		{"security@gITea.io", true},
 | 
							{"security@gITea.io", true},
 | 
				
			||||||
		{"hdudhdd", false},
 | 
							{"invalid", false},
 | 
				
			||||||
		{"seee@example.com", false},
 | 
							{"seee@example.com", false},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							{"user@my.allow", true},
 | 
				
			||||||
 | 
							{"user@my.allow1", false},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, v := range tt {
 | 
						for _, v := range tt {
 | 
				
			||||||
@@ -64,11 +77,14 @@ func TestRegisterForm_IsDomainAllowed_WhitelistedEmail(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestRegisterForm_IsDomainAllowed_BlocklistedEmail(t *testing.T) {
 | 
					func TestRegisterForm_IsDomainAllowed_BlockedEmail(t *testing.T) {
 | 
				
			||||||
	_ = setting.Service
 | 
						oldService := setting.Service
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							setting.Service = oldService
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	setting.Service.EmailDomainWhitelist = []string{}
 | 
						setting.Service.EmailDomainAllowList = nil
 | 
				
			||||||
	setting.Service.EmailDomainBlocklist = []string{"gitea.io"}
 | 
						setting.Service.EmailDomainBlockList = []glob.Glob{glob.MustCompile("gitea.io"), glob.MustCompile("*.block")}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tt := []struct {
 | 
						tt := []struct {
 | 
				
			||||||
		email string
 | 
							email string
 | 
				
			||||||
@@ -76,7 +92,10 @@ func TestRegisterForm_IsDomainAllowed_BlocklistedEmail(t *testing.T) {
 | 
				
			|||||||
	}{
 | 
						}{
 | 
				
			||||||
		{"security@gitea.io", false},
 | 
							{"security@gitea.io", false},
 | 
				
			||||||
		{"security@gitea.example", true},
 | 
							{"security@gitea.example", true},
 | 
				
			||||||
		{"hdudhdd", true},
 | 
							{"invalid", true},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							{"user@my.block", false},
 | 
				
			||||||
 | 
							{"user@my.block1", true},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, v := range tt {
 | 
						for _, v := range tt {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user