enhance: add configuration to allow custom characters in names (#42417) (#44063)

related: #42417
    
- Add NameValidationAllowedChars and RoleNameValidationAllowedChars
  configuration parameters to specify additional characters allowed
  respectively in (generic) names and a role names
- All validations in validateName method is moved to a the new method
  validateNameWithCustomChars which is called by both validateName
  and ValidateRoleName while specifying characters allowed

Signed-off-by: Jean-Francois Weber-Marx <jfwm@hotmail.com>
Signed-off-by: Jean-Francois Weber-Marx <jf.webermarx@criteo.com>
This commit is contained in:
Jean-Francois Weber-Marx 2025-09-02 03:57:52 +00:00 committed by GitHub
parent 9e2d1963d4
commit 330a871979
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 80 additions and 37 deletions

View File

@ -316,6 +316,10 @@ proxy:
# please adjust in embedded Milvus: false
ginLogging: true
ginLogSkipPaths: / # skip url path for gin log
nameValidation:
allowedChars: $ # Additional characters allowed in names beyond underscores, letters and numbers. To allow hyphens in names, add '-' here.
roleNameValidation:
allowedChars: $ # Additional characters allowed in role names beyond underscores, letters and numbers. Add '-' to allow hyphens in role names.
maxTaskNum: 1024 # The maximum number of tasks in the task queue of the proxy.
ddlConcurrency: 16 # The concurrent execution number of DDL at proxy.
dclConcurrency: 16 # The concurrent execution number of DCL at proxy.

View File

@ -1283,6 +1283,10 @@ func getMaxMvccTsFromChannels(channelsTs map[string]uint64, beginTs typeutil.Tim
}
func validateName(entity string, nameType string) error {
return validateNameWithCustomChars(entity, nameType, Params.ProxyCfg.NameValidationAllowedChars.GetValue())
}
func validateNameWithCustomChars(entity string, nameType string, allowedChars string) error {
entity = strings.TrimSpace(entity)
if entity == "" {
@ -1305,15 +1309,15 @@ func validateName(entity string, nameType string) error {
for i := 1; i < len(entity); i++ {
c := entity[i]
if c != '_' && c != '$' && !isAlpha(c) && !isNumber(c) {
return merr.WrapErrParameterInvalidMsg("%s can only contain numbers, letters, dollars and underscores, found %c at %d", nameType, c, i)
if c != '_' && !isAlpha(c) && !isNumber(c) && !strings.ContainsRune(allowedChars, rune(c)) {
return merr.WrapErrParameterInvalidMsg("%s can only contain numbers, letters, underscores, and allowed characters (%s), found %c at %d", nameType, allowedChars, c, i)
}
}
return nil
}
func ValidateRoleName(entity string) error {
return validateName(entity, "role name")
return validateNameWithCustomChars(entity, "role name", Params.ProxyCfg.RoleNameValidationAllowedChars.GetValue())
}
func IsDefaultRole(roleName string) bool {

View File

@ -708,6 +708,23 @@ func TestValidateName(t *testing.T) {
assert.Nil(t, ValidateObjectName("*"))
}
func TestValidateRoleName_HyphenToggle(t *testing.T) {
pt := paramtable.Get()
pt.ProxyCfg.RoleNameValidationAllowedChars.SwapTempValue("$-")
assert.Nil(t, ValidateRoleName("Admin-1"))
assert.Nil(t, ValidateRoleName("_a-bc$1"))
assert.NotNil(t, ValidateRoleName("-bad"))
assert.NotNil(t, ValidateRoleName("1leading"))
assert.NotNil(t, ValidateRoleName(""))
assert.NotNil(t, ValidateRoleName("*"))
pt.ProxyCfg.RoleNameValidationAllowedChars.SwapTempValue("$")
assert.Nil(t, ValidateRoleName("Admin_1"))
assert.Nil(t, ValidateRoleName("Admin$1"))
assert.NotNil(t, ValidateRoleName("Admin-1"))
}
func TestIsDefaultRole(t *testing.T) {
assert.Equal(t, true, IsDefaultRole(util.RoleAdmin))
assert.Equal(t, true, IsDefaultRole(util.RolePublic))

View File

@ -1627,40 +1627,42 @@ type proxyConfig struct {
// Alias string
SoPath ParamItem `refreshable:"false"`
TimeTickInterval ParamItem `refreshable:"false"`
HealthCheckTimeout ParamItem `refreshable:"true"`
MsgStreamTimeTickBufSize ParamItem `refreshable:"true"`
MaxNameLength ParamItem `refreshable:"true"`
MaxUsernameLength ParamItem `refreshable:"true"`
MinPasswordLength ParamItem `refreshable:"true"`
MaxPasswordLength ParamItem `refreshable:"true"`
MaxFieldNum ParamItem `refreshable:"true"`
MaxVectorFieldNum ParamItem `refreshable:"true"`
MaxShardNum ParamItem `refreshable:"true"`
MaxDimension ParamItem `refreshable:"true"`
GinLogging ParamItem `refreshable:"false"`
GinLogSkipPaths ParamItem `refreshable:"false"`
MaxUserNum ParamItem `refreshable:"true"`
MaxRoleNum ParamItem `refreshable:"true"`
MaxTaskNum ParamItem `refreshable:"false"`
DDLConcurrency ParamItem `refreshable:"true"`
DCLConcurrency ParamItem `refreshable:"true"`
ShardLeaderCacheInterval ParamItem `refreshable:"false"`
ReplicaSelectionPolicy ParamItem `refreshable:"false"`
CheckQueryNodeHealthInterval ParamItem `refreshable:"false"`
CostMetricsExpireTime ParamItem `refreshable:"false"`
CheckWorkloadRequestNum ParamItem `refreshable:"false"`
WorkloadToleranceFactor ParamItem `refreshable:"false"`
RetryTimesOnReplica ParamItem `refreshable:"true"`
RetryTimesOnHealthCheck ParamItem `refreshable:"true"`
PartitionNameRegexp ParamItem `refreshable:"true"`
MustUsePartitionKey ParamItem `refreshable:"true"`
SkipAutoIDCheck ParamItem `refreshable:"true"`
SkipPartitionKeyCheck ParamItem `refreshable:"true"`
MaxVarCharLength ParamItem `refreshable:"false"`
MaxTextLength ParamItem `refreshable:"false"`
MaxResultEntries ParamItem `refreshable:"true"`
EnableCachedServiceProvider ParamItem `refreshable:"true"`
TimeTickInterval ParamItem `refreshable:"false"`
HealthCheckTimeout ParamItem `refreshable:"true"`
MsgStreamTimeTickBufSize ParamItem `refreshable:"true"`
MaxNameLength ParamItem `refreshable:"true"`
MaxUsernameLength ParamItem `refreshable:"true"`
MinPasswordLength ParamItem `refreshable:"true"`
MaxPasswordLength ParamItem `refreshable:"true"`
MaxFieldNum ParamItem `refreshable:"true"`
MaxVectorFieldNum ParamItem `refreshable:"true"`
MaxShardNum ParamItem `refreshable:"true"`
MaxDimension ParamItem `refreshable:"true"`
GinLogging ParamItem `refreshable:"false"`
GinLogSkipPaths ParamItem `refreshable:"false"`
MaxUserNum ParamItem `refreshable:"true"`
MaxRoleNum ParamItem `refreshable:"true"`
NameValidationAllowedChars ParamItem `refreshable:"true"`
RoleNameValidationAllowedChars ParamItem `refreshable:"true"`
MaxTaskNum ParamItem `refreshable:"false"`
DDLConcurrency ParamItem `refreshable:"true"`
DCLConcurrency ParamItem `refreshable:"true"`
ShardLeaderCacheInterval ParamItem `refreshable:"false"`
ReplicaSelectionPolicy ParamItem `refreshable:"false"`
CheckQueryNodeHealthInterval ParamItem `refreshable:"false"`
CostMetricsExpireTime ParamItem `refreshable:"false"`
CheckWorkloadRequestNum ParamItem `refreshable:"false"`
WorkloadToleranceFactor ParamItem `refreshable:"false"`
RetryTimesOnReplica ParamItem `refreshable:"true"`
RetryTimesOnHealthCheck ParamItem `refreshable:"true"`
PartitionNameRegexp ParamItem `refreshable:"true"`
MustUsePartitionKey ParamItem `refreshable:"true"`
SkipAutoIDCheck ParamItem `refreshable:"true"`
SkipPartitionKeyCheck ParamItem `refreshable:"true"`
MaxVarCharLength ParamItem `refreshable:"false"`
MaxTextLength ParamItem `refreshable:"false"`
MaxResultEntries ParamItem `refreshable:"true"`
EnableCachedServiceProvider ParamItem `refreshable:"true"`
AccessLog AccessLogConfig
@ -1855,6 +1857,22 @@ please adjust in embedded Milvus: false`,
}
p.MaxRoleNum.Init(base.mgr)
p.NameValidationAllowedChars = ParamItem{
Key: "proxy.nameValidation.allowedChars",
DefaultValue: "$",
Doc: "Additional characters allowed in names beyond underscores, letters and numbers. To allow hyphens in names, add '-' here.",
Export: true,
}
p.NameValidationAllowedChars.Init(base.mgr)
p.RoleNameValidationAllowedChars = ParamItem{
Key: "proxy.roleNameValidation.allowedChars",
DefaultValue: "$",
Doc: "Additional characters allowed in role names beyond underscores, letters and numbers. Add '-' to allow hyphens in role names.",
Export: true,
}
p.RoleNameValidationAllowedChars.Init(base.mgr)
p.SoPath = ParamItem{
Key: "proxy.soPath",
Version: "2.2.0",