mirror of
				https://codeberg.org/forgejo/forgejo.git
				synced 2025-10-31 06:21:11 +00:00 
			
		
		
		
	- Add a new setting `EMAIL_DOMAIN_BLOCK_DISPOSABLE` that will append a list of domains that are known for being used by temporary or disposable email services. - Add a utility to automatically download and format the list of domains from the disposable-email-domains project on github. (https://github.com/disposable-email-domains/disposable-email-domains) license: CC0 1.0 Universal (CC0 1.0) [Public Domain] from README: """ This repo contains a list of disposable and temporary email address domains often used to register dummy users in order to spam or abuse some services. We cannot guarantee all of these can still be considered disposable but we do basic checking so chances are they were disposable at one point in time. """
		
			
				
	
	
		
			252 lines
		
	
	
	
		
			6.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			252 lines
		
	
	
	
		
			6.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2023 The Gitea Authors. All rights reserved.
 | |
| // SPDX-License-Identifier: MIT
 | |
| 
 | |
| package setting
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"sort"
 | |
| 	"strings"
 | |
| 	"testing"
 | |
| 
 | |
| 	"code.gitea.io/gitea/modules/structs"
 | |
| 
 | |
| 	"github.com/gobwas/glob"
 | |
| 	"github.com/stretchr/testify/assert"
 | |
| 	"github.com/stretchr/testify/require"
 | |
| 	"golang.org/x/net/publicsuffix"
 | |
| )
 | |
| 
 | |
| func match(globs []glob.Glob, s string) bool {
 | |
| 	for _, g := range globs {
 | |
| 		if g.Match(s) {
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| 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
 | |
| `)
 | |
| 	require.NoError(t, err)
 | |
| 	loadServiceFrom(cfg)
 | |
| 
 | |
| 	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"))
 | |
| }
 | |
| 
 | |
| func TestLoadServiceBlockDisposable(t *testing.T) {
 | |
| 	oldService := Service
 | |
| 	defer func() {
 | |
| 		Service = oldService
 | |
| 	}()
 | |
| 
 | |
| 	cfg, err := NewConfigProviderFromData(`
 | |
| [service]
 | |
| EMAIL_DOMAIN_BLOCK_DISPOSABLE = true
 | |
| `)
 | |
| 
 | |
| 	require.NoError(t, err)
 | |
| 	loadServiceFrom(cfg)
 | |
| 
 | |
| 	for _, domain := range DisposableEmailDomains() {
 | |
| 		require.True(t, match(Service.EmailDomainBlockList, domain))
 | |
| 	}
 | |
| 
 | |
| 	require.Len(t, Service.EmailDomainBlockList, len(DisposableEmailDomains()))
 | |
| 
 | |
| 	knownGood := [...]string{
 | |
| 		"aol.com",
 | |
| 		"gmx.com",
 | |
| 		"mail.com",
 | |
| 		"zoho.com",
 | |
| 		"proton.me",
 | |
| 		"gmail.com",
 | |
| 		"yahoo.com",
 | |
| 		"icloud.com",
 | |
| 		"outlook.com",
 | |
| 		"protonmail.com",
 | |
| 	}
 | |
| 
 | |
| 	for _, domain := range knownGood {
 | |
| 		require.False(t, match(Service.EmailDomainBlockList, domain))
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestLoadServiceBlockDisposableWithExistingGlobs(t *testing.T) {
 | |
| 	oldService := Service
 | |
| 	defer func() {
 | |
| 		Service = oldService
 | |
| 	}()
 | |
| 
 | |
| 	tldCounts := make(map[string]int)
 | |
| 	for _, domain := range DisposableEmailDomains() {
 | |
| 		tld, _ := publicsuffix.PublicSuffix(domain)
 | |
| 		tldCounts[tld]++
 | |
| 	}
 | |
| 
 | |
| 	type tldkv struct {
 | |
| 		Tld   string
 | |
| 		Count int
 | |
| 	}
 | |
| 
 | |
| 	sortedTldCounts := make([]tldkv, 0)
 | |
| 	for tld, count := range tldCounts {
 | |
| 		sortedTldCounts = append(sortedTldCounts, tldkv{tld, count})
 | |
| 	}
 | |
| 
 | |
| 	sort.Slice(sortedTldCounts, func(i, j int) bool {
 | |
| 		return sortedTldCounts[i].Count > sortedTldCounts[j].Count
 | |
| 	})
 | |
| 	require.GreaterOrEqual(t, len(sortedTldCounts), 2)
 | |
| 
 | |
| 	blockString := fmt.Sprintf("*.%s,*.%s", sortedTldCounts[0].Tld, sortedTldCounts[1].Tld)
 | |
| 
 | |
| 	cfg, err := NewConfigProviderFromData(fmt.Sprintf(`
 | |
| [service]
 | |
| EMAIL_DOMAIN_BLOCKLIST = %s
 | |
| EMAIL_DOMAIN_BLOCK_DISPOSABLE = true
 | |
| `, blockString))
 | |
| 
 | |
| 	require.NoError(t, err)
 | |
| 	loadServiceFrom(cfg)
 | |
| 
 | |
| 	for _, domain := range DisposableEmailDomains() {
 | |
| 		require.True(t, match(Service.EmailDomainBlockList, domain))
 | |
| 	}
 | |
| 
 | |
| 	redundant := 0
 | |
| 	for _, val := range DisposableEmailDomains() {
 | |
| 		if strings.HasSuffix(val, sortedTldCounts[0].Tld) ||
 | |
| 			strings.HasSuffix(val, sortedTldCounts[1].Tld) {
 | |
| 			redundant++
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	expected := len(DisposableEmailDomains()) - redundant + 2
 | |
| 	require.Len(t, Service.EmailDomainBlockList, expected)
 | |
| }
 | |
| 
 | |
| func TestLoadServiceBlockDisposableWithComplementGlobs(t *testing.T) {
 | |
| 	oldService := Service
 | |
| 	defer func() {
 | |
| 		Service = oldService
 | |
| 	}()
 | |
| 
 | |
| 	cfg, err := NewConfigProviderFromData(`
 | |
| [service]
 | |
| EMAIL_DOMAIN_BLOCKLIST = *.random
 | |
| EMAIL_DOMAIN_BLOCK_DISPOSABLE = true
 | |
| `)
 | |
| 
 | |
| 	require.NoError(t, err)
 | |
| 	loadServiceFrom(cfg)
 | |
| 
 | |
| 	for _, domain := range DisposableEmailDomains() {
 | |
| 		require.True(t, match(Service.EmailDomainBlockList, domain))
 | |
| 	}
 | |
| 
 | |
| 	expected := len(DisposableEmailDomains()) + 1
 | |
| 	require.Len(t, Service.EmailDomainBlockList, expected)
 | |
| }
 | |
| 
 | |
| func TestLoadServiceVisibilityModes(t *testing.T) {
 | |
| 	oldService := Service
 | |
| 	defer func() {
 | |
| 		Service = oldService
 | |
| 	}()
 | |
| 
 | |
| 	kases := map[string]func(){
 | |
| 		`
 | |
| [service]
 | |
| DEFAULT_USER_VISIBILITY = public
 | |
| ALLOWED_USER_VISIBILITY_MODES = public,limited,private
 | |
| `: func() {
 | |
| 			assert.Equal(t, "public", Service.DefaultUserVisibility)
 | |
| 			assert.Equal(t, structs.VisibleTypePublic, Service.DefaultUserVisibilityMode)
 | |
| 			assert.Equal(t, []string{"public", "limited", "private"}, Service.AllowedUserVisibilityModes)
 | |
| 		},
 | |
| 		`
 | |
| 		[service]
 | |
| 		DEFAULT_USER_VISIBILITY = public
 | |
| 		`: func() {
 | |
| 			assert.Equal(t, "public", Service.DefaultUserVisibility)
 | |
| 			assert.Equal(t, structs.VisibleTypePublic, Service.DefaultUserVisibilityMode)
 | |
| 			assert.Equal(t, []string{"public", "limited", "private"}, Service.AllowedUserVisibilityModes)
 | |
| 		},
 | |
| 		`
 | |
| 		[service]
 | |
| 		DEFAULT_USER_VISIBILITY = limited
 | |
| 		`: func() {
 | |
| 			assert.Equal(t, "limited", Service.DefaultUserVisibility)
 | |
| 			assert.Equal(t, structs.VisibleTypeLimited, Service.DefaultUserVisibilityMode)
 | |
| 			assert.Equal(t, []string{"public", "limited", "private"}, Service.AllowedUserVisibilityModes)
 | |
| 		},
 | |
| 		`
 | |
| [service]
 | |
| ALLOWED_USER_VISIBILITY_MODES = public,limited,private
 | |
| `: func() {
 | |
| 			assert.Equal(t, "public", Service.DefaultUserVisibility)
 | |
| 			assert.Equal(t, structs.VisibleTypePublic, Service.DefaultUserVisibilityMode)
 | |
| 			assert.Equal(t, []string{"public", "limited", "private"}, Service.AllowedUserVisibilityModes)
 | |
| 		},
 | |
| 		`
 | |
| [service]
 | |
| DEFAULT_USER_VISIBILITY = public
 | |
| ALLOWED_USER_VISIBILITY_MODES = limited,private
 | |
| `: func() {
 | |
| 			assert.Equal(t, "limited", Service.DefaultUserVisibility)
 | |
| 			assert.Equal(t, structs.VisibleTypeLimited, Service.DefaultUserVisibilityMode)
 | |
| 			assert.Equal(t, []string{"limited", "private"}, Service.AllowedUserVisibilityModes)
 | |
| 		},
 | |
| 		`
 | |
| [service]
 | |
| DEFAULT_USER_VISIBILITY = my_type
 | |
| ALLOWED_USER_VISIBILITY_MODES = limited,private
 | |
| `: func() {
 | |
| 			assert.Equal(t, "limited", Service.DefaultUserVisibility)
 | |
| 			assert.Equal(t, structs.VisibleTypeLimited, Service.DefaultUserVisibilityMode)
 | |
| 			assert.Equal(t, []string{"limited", "private"}, Service.AllowedUserVisibilityModes)
 | |
| 		},
 | |
| 		`
 | |
| [service]
 | |
| DEFAULT_USER_VISIBILITY = public
 | |
| ALLOWED_USER_VISIBILITY_MODES = public, limit, privated
 | |
| `: func() {
 | |
| 			assert.Equal(t, "public", Service.DefaultUserVisibility)
 | |
| 			assert.Equal(t, structs.VisibleTypePublic, Service.DefaultUserVisibilityMode)
 | |
| 			assert.Equal(t, []string{"public"}, Service.AllowedUserVisibilityModes)
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for kase, fun := range kases {
 | |
| 		t.Run(kase, func(t *testing.T) {
 | |
| 			cfg, err := NewConfigProviderFromData(kase)
 | |
| 			require.NoError(t, err)
 | |
| 			loadServiceFrom(cfg)
 | |
| 			fun()
 | |
| 			// reset
 | |
| 			Service.AllowedUserVisibilityModesSlice = []bool{true, true, true}
 | |
| 			Service.AllowedUserVisibilityModes = []string{}
 | |
| 			Service.DefaultUserVisibility = ""
 | |
| 			Service.DefaultUserVisibilityMode = structs.VisibleTypePublic
 | |
| 		})
 | |
| 	}
 | |
| }
 |