mirror of
				https://codeberg.org/forgejo/forgejo.git
				synced 2025-10-30 22:11:07 +00:00 
			
		
		
		
	[GITEA] add option for banning dots in usernames
Refs: https://codeberg.org/forgejo/forgejo/pulls/676 Author: Panagiotis "Ivory" Vasilopoulos <git@n0toose.net> Date: Mon Jun 12 13:57:01 2023 +0200 Co-authored-by: Gusted <postmaster@gusted.xyz> (cherry picked from commitfabdda5c6e) (cherry picked from commitd2c7f45621) (cherry picked from commitdfdbaba3d6) (cherry picked from commita3cda092b8)
This commit is contained in:
		
					parent
					
						
							
								188226a8e6
							
						
					
				
			
			
				commit
				
					
						f0fdb5905c
					
				
			
		
					 7 changed files with 57 additions and 5 deletions
				
			
		|  | @ -801,6 +801,11 @@ LEVEL = Info | ||||||
| ;; Every new user will have restricted permissions depending on this setting | ;; Every new user will have restricted permissions depending on this setting | ||||||
| ;DEFAULT_USER_IS_RESTRICTED = false | ;DEFAULT_USER_IS_RESTRICTED = false | ||||||
| ;; | ;; | ||||||
|  | ;; Users will be able to use dots when choosing their username. Disabling this is | ||||||
|  | ;; helpful if your usersare having issues with e.g. RSS feeds or advanced third-party | ||||||
|  | ;; extensions that use strange regex patterns. | ||||||
|  | ; ALLOW_DOTS_IN_USERNAMES = true | ||||||
|  | ;; | ||||||
| ;; Either "public", "limited" or "private", default is "public" | ;; Either "public", "limited" or "private", default is "public" | ||||||
| ;; Limited is for users visible only to signed users | ;; Limited is for users visible only to signed users | ||||||
| ;; Private is for users visible only to members of their organizations | ;; Private is for users visible only to members of their organizations | ||||||
|  |  | ||||||
|  | @ -67,6 +67,7 @@ var Service = struct { | ||||||
| 	DefaultKeepEmailPrivate                 bool | 	DefaultKeepEmailPrivate                 bool | ||||||
| 	DefaultAllowCreateOrganization          bool | 	DefaultAllowCreateOrganization          bool | ||||||
| 	DefaultUserIsRestricted                 bool | 	DefaultUserIsRestricted                 bool | ||||||
|  | 	AllowDotsInUsernames                    bool | ||||||
| 	EnableTimetracking                      bool | 	EnableTimetracking                      bool | ||||||
| 	DefaultEnableTimetracking               bool | 	DefaultEnableTimetracking               bool | ||||||
| 	DefaultEnableDependencies               bool | 	DefaultEnableDependencies               bool | ||||||
|  | @ -177,6 +178,7 @@ func loadServiceFrom(rootCfg ConfigProvider) { | ||||||
| 	Service.DefaultKeepEmailPrivate = sec.Key("DEFAULT_KEEP_EMAIL_PRIVATE").MustBool() | 	Service.DefaultKeepEmailPrivate = sec.Key("DEFAULT_KEEP_EMAIL_PRIVATE").MustBool() | ||||||
| 	Service.DefaultAllowCreateOrganization = sec.Key("DEFAULT_ALLOW_CREATE_ORGANIZATION").MustBool(true) | 	Service.DefaultAllowCreateOrganization = sec.Key("DEFAULT_ALLOW_CREATE_ORGANIZATION").MustBool(true) | ||||||
| 	Service.DefaultUserIsRestricted = sec.Key("DEFAULT_USER_IS_RESTRICTED").MustBool(false) | 	Service.DefaultUserIsRestricted = sec.Key("DEFAULT_USER_IS_RESTRICTED").MustBool(false) | ||||||
|  | 	Service.AllowDotsInUsernames = sec.Key("ALLOW_DOTS_IN_USERNAMES").MustBool(true) | ||||||
| 	Service.EnableTimetracking = sec.Key("ENABLE_TIMETRACKING").MustBool(true) | 	Service.EnableTimetracking = sec.Key("ENABLE_TIMETRACKING").MustBool(true) | ||||||
| 	if Service.EnableTimetracking { | 	if Service.EnableTimetracking { | ||||||
| 		Service.DefaultEnableTimetracking = sec.Key("DEFAULT_ENABLE_TIMETRACKING").MustBool(true) | 		Service.DefaultEnableTimetracking = sec.Key("DEFAULT_ENABLE_TIMETRACKING").MustBool(true) | ||||||
|  |  | ||||||
|  | @ -92,13 +92,20 @@ func IsValidExternalTrackerURLFormat(uri string) bool { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
| 	validUsernamePattern   = regexp.MustCompile(`^[\da-zA-Z][-.\w]*$`) | 	validUsernamePatternWithDots    = regexp.MustCompile(`^[\da-zA-Z][-.\w]*$`) | ||||||
| 	invalidUsernamePattern = regexp.MustCompile(`[-._]{2,}|[-._]$`) // No consecutive or trailing non-alphanumeric chars | 	validUsernamePatternWithoutDots = regexp.MustCompile(`^[\da-zA-Z][-\w]*$`) | ||||||
|  | 
 | ||||||
|  | 	// No consecutive or trailing non-alphanumeric chars, catches both cases | ||||||
|  | 	invalidUsernamePattern = regexp.MustCompile(`[-._]{2,}|[-._]$`) | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // IsValidUsername checks if username is valid | // IsValidUsername checks if username is valid | ||||||
| func IsValidUsername(name string) bool { | func IsValidUsername(name string) bool { | ||||||
| 	// It is difficult to find a single pattern that is both readable and effective, | 	// It is difficult to find a single pattern that is both readable and effective, | ||||||
| 	// but it's easier to use positive and negative checks. | 	// but it's easier to use positive and negative checks. | ||||||
| 	return validUsernamePattern.MatchString(name) && !invalidUsernamePattern.MatchString(name) | 	if setting.Service.AllowDotsInUsernames { | ||||||
|  | 		return validUsernamePatternWithDots.MatchString(name) && !invalidUsernamePattern.MatchString(name) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return validUsernamePatternWithoutDots.MatchString(name) && !invalidUsernamePattern.MatchString(name) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -155,7 +155,8 @@ func Test_IsValidExternalTrackerURLFormat(t *testing.T) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestIsValidUsername(t *testing.T) { | func TestIsValidUsernameAllowDots(t *testing.T) { | ||||||
|  | 	setting.Service.AllowDotsInUsernames = true | ||||||
| 	tests := []struct { | 	tests := []struct { | ||||||
| 		arg  string | 		arg  string | ||||||
| 		want bool | 		want bool | ||||||
|  | @ -185,3 +186,31 @@ func TestIsValidUsername(t *testing.T) { | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func TestIsValidUsernameBanDots(t *testing.T) { | ||||||
|  | 	setting.Service.AllowDotsInUsernames = false | ||||||
|  | 	defer func() { | ||||||
|  | 		setting.Service.AllowDotsInUsernames = true | ||||||
|  | 	}() | ||||||
|  | 
 | ||||||
|  | 	tests := []struct { | ||||||
|  | 		arg  string | ||||||
|  | 		want bool | ||||||
|  | 	}{ | ||||||
|  | 		{arg: "a", want: true}, | ||||||
|  | 		{arg: "abc", want: true}, | ||||||
|  | 		{arg: "0.b-c", want: false}, | ||||||
|  | 		{arg: "a.b-c_d", want: false}, | ||||||
|  | 		{arg: ".abc", want: false}, | ||||||
|  | 		{arg: "abc.", want: false}, | ||||||
|  | 		{arg: "a..bc", want: false}, | ||||||
|  | 		{arg: "a...bc", want: false}, | ||||||
|  | 		{arg: "a.-bc", want: false}, | ||||||
|  | 		{arg: "a._bc", want: false}, | ||||||
|  | 	} | ||||||
|  | 	for _, tt := range tests { | ||||||
|  | 		t.Run(tt.arg, func(t *testing.T) { | ||||||
|  | 			assert.Equalf(t, tt.want, IsValidUsername(tt.arg), "IsValidUsername[AllowDotsInUsernames=false](%v)", tt.arg) | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ import ( | ||||||
| 	"reflect" | 	"reflect" | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
|  | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/modules/translation" | 	"code.gitea.io/gitea/modules/translation" | ||||||
| 	"code.gitea.io/gitea/modules/util" | 	"code.gitea.io/gitea/modules/util" | ||||||
| 	"code.gitea.io/gitea/modules/validation" | 	"code.gitea.io/gitea/modules/validation" | ||||||
|  | @ -135,7 +136,11 @@ func Validate(errs binding.Errors, data map[string]any, f Form, l translation.Lo | ||||||
| 			case validation.ErrRegexPattern: | 			case validation.ErrRegexPattern: | ||||||
| 				data["ErrorMsg"] = trName + l.Tr("form.regex_pattern_error", errs[0].Message) | 				data["ErrorMsg"] = trName + l.Tr("form.regex_pattern_error", errs[0].Message) | ||||||
| 			case validation.ErrUsername: | 			case validation.ErrUsername: | ||||||
| 				data["ErrorMsg"] = trName + l.Tr("form.username_error") | 				if setting.Service.AllowDotsInUsernames { | ||||||
|  | 					data["ErrorMsg"] = trName + l.Tr("form.username_error") | ||||||
|  | 				} else { | ||||||
|  | 					data["ErrorMsg"] = trName + l.Tr("form.username_error_no_dots") | ||||||
|  | 				} | ||||||
| 			case validation.ErrInvalidGroupTeamMap: | 			case validation.ErrInvalidGroupTeamMap: | ||||||
| 				data["ErrorMsg"] = trName + l.Tr("form.invalid_group_team_map_error", errs[0].Message) | 				data["ErrorMsg"] = trName + l.Tr("form.invalid_group_team_map_error", errs[0].Message) | ||||||
| 			default: | 			default: | ||||||
|  |  | ||||||
|  | @ -289,6 +289,7 @@ default_allow_create_organization = Allow Creation of Organizations by Default | ||||||
| default_allow_create_organization_popup = Allow new user accounts to create organizations by default. | default_allow_create_organization_popup = Allow new user accounts to create organizations by default. | ||||||
| default_enable_timetracking = Enable Time Tracking by Default | default_enable_timetracking = Enable Time Tracking by Default | ||||||
| default_enable_timetracking_popup = Enable time tracking for new repositories by default. | default_enable_timetracking_popup = Enable time tracking for new repositories by default. | ||||||
|  | allow_dots_in_usernames = Allow users to use dots in their usernames. Doesn't affect existing accounts. | ||||||
| no_reply_address = Hidden Email Domain | no_reply_address = Hidden Email Domain | ||||||
| no_reply_address_helper = Domain name for users with a hidden email address. For example, the username 'joe' will be logged in Git as 'joe@noreply.example.org' if the hidden email domain is set to 'noreply.example.org'. | no_reply_address_helper = Domain name for users with a hidden email address. For example, the username 'joe' will be logged in Git as 'joe@noreply.example.org' if the hidden email domain is set to 'noreply.example.org'. | ||||||
| password_algorithm = Password Hash Algorithm | password_algorithm = Password Hash Algorithm | ||||||
|  | @ -527,6 +528,7 @@ include_error = ` must contain substring "%s".` | ||||||
| glob_pattern_error = ` glob pattern is invalid: %s.` | glob_pattern_error = ` glob pattern is invalid: %s.` | ||||||
| regex_pattern_error = ` regex pattern is invalid: %s.` | regex_pattern_error = ` regex pattern is invalid: %s.` | ||||||
| username_error = ` can only contain alphanumeric chars ('0-9','a-z','A-Z'), dash ('-'), underscore ('_') and dot ('.'). It cannot begin or end with non-alphanumeric chars, and consecutive non-alphanumeric chars are also forbidden.` | username_error = ` can only contain alphanumeric chars ('0-9','a-z','A-Z'), dash ('-'), underscore ('_') and dot ('.'). It cannot begin or end with non-alphanumeric chars, and consecutive non-alphanumeric chars are also forbidden.` | ||||||
|  | username_error_no_dots = ` can only contain alphanumeric chars ('0-9','a-z','A-Z'), dash ('-') and underscore ('_'). It cannot begin or end with non-alphanumeric chars, and consecutive non-alphanumeric chars are also forbidden.` | ||||||
| invalid_group_team_map_error = ` mapping is invalid: %s` | invalid_group_team_map_error = ` mapping is invalid: %s` | ||||||
| unknown_error = Unknown error: | unknown_error = Unknown error: | ||||||
| captcha_incorrect = The CAPTCHA code is incorrect. | captcha_incorrect = The CAPTCHA code is incorrect. | ||||||
|  |  | ||||||
|  | @ -159,6 +159,8 @@ | ||||||
| 				<dd>{{if .Service.DefaultKeepEmailPrivate}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd> | 				<dd>{{if .Service.DefaultKeepEmailPrivate}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd> | ||||||
| 				<dt>{{.locale.Tr "admin.config.default_allow_create_organization"}}</dt> | 				<dt>{{.locale.Tr "admin.config.default_allow_create_organization"}}</dt> | ||||||
| 				<dd>{{if .Service.DefaultAllowCreateOrganization}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd> | 				<dd>{{if .Service.DefaultAllowCreateOrganization}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd> | ||||||
|  | 				<dt>{{.locale.Tr "admin.config.allow_dots_in_usernames"}}</dt> | ||||||
|  | 				<dd>{{if .Service.AllowDotsInUsernames}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd> | ||||||
| 				<dt>{{.locale.Tr "admin.config.enable_timetracking"}}</dt> | 				<dt>{{.locale.Tr "admin.config.enable_timetracking"}}</dt> | ||||||
| 				<dd>{{if .Service.EnableTimetracking}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd> | 				<dd>{{if .Service.EnableTimetracking}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd> | ||||||
| 				{{if .Service.EnableTimetracking}} | 				{{if .Service.EnableTimetracking}} | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue