mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-11-22 08:42:32 -05: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
) (cherry picked from commitf0fdb5905c
) (cherry picked from commit9697e48c1f
) (cherry picked from commit46e31009a8
) (cherry picked from commit5bb2c54b6f
) (cherry picked from commit682f9d24e1
) (cherry picked from commit1863481005
) (cherry picked from commit4f1b7c4ddb
) (cherry picked from commit6afe70bbf1
) (cherry picked from commit5cec1d9c2d
) Conflicts: templates/admin/config.tmpl https://codeberg.org/forgejo/forgejo/pulls/1512 (cherry picked from commitde2d172473
) (cherry picked from commit37a3172dd9
) (cherry picked from commit92dfca0c5a
) (cherry picked from commita713d59b0c
) (cherry picked from commitbf18b10982
) (cherry picked from commit11d77f40a1
) (cherry picked from commit17ca5ff2d6
) (cherry picked from commitfc50a7f74e
)
This commit is contained in:
parent
7d86cbbba1
commit
97496c6c62
7 changed files with 57 additions and 5 deletions
|
@ -808,6 +808,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
|
||||||
|
|
|
@ -68,6 +68,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
|
||||||
|
@ -180,6 +181,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)
|
||||||
|
|
|
@ -117,13 +117,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:
|
||||||
|
|
|
@ -294,6 +294,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
|
||||||
|
@ -532,6 +533,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>{{ctx.Locale.Tr "admin.config.default_allow_create_organization"}}</dt>
|
<dt>{{ctx.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>{{ctx.Locale.Tr "admin.config.allow_dots_in_usernames"}}</dt>
|
||||||
|
<dd>{{if .Service.AllowDotsInUsernames}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
|
||||||
<dt>{{ctx.Locale.Tr "admin.config.enable_timetracking"}}</dt>
|
<dt>{{ctx.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…
Reference in a new issue