This is an automated email from the ASF dual-hosted git repository. morningman pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push: new 0993bd5723e [fix](auth)Fixed the issue of not adding escape characters in the regular code, which allowed usernames to use colons (#49459) 0993bd5723e is described below commit 0993bd5723e86849654f04730df94bf0d5f62281 Author: zhangdong <zhangd...@selectdb.com> AuthorDate: Wed Apr 2 17:29:01 2025 +0800 [fix](auth)Fixed the issue of not adding escape characters in the regular code, which allowed usernames to use colons (#49459) ### What problem does this PR solve? Due to incorrect regularization, users are able to create a user named default_cluster: xxx. When replaying logs, compatibility operations will automatically remove the default_cluster prefix, which may cause users to duplicate It doesn't affect whether escape characters are added or not, except for USER_NAME_REGEX, because in other regularization, the position of - doesn't have a special meaning. But for better understanding, all regular - characters are uniformly prefixed with escape characters, Issue Number: close #xxx Related PR: #xxx Problem Summary: Fixed the issue of not adding escape characters in the regular code, which allowed usernames to use colons ### Release note Fixed the issue of not adding escape characters in the regular code, which allowed usernames to use colons --- .../java/org/apache/doris/common/FeNameFormat.java | 30 +- .../org/apache/doris/common/FeNameFormatTest.java | 330 ++++++++++++++++++--- 2 files changed, 307 insertions(+), 53 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/common/FeNameFormat.java b/fe/fe-core/src/main/java/org/apache/doris/common/FeNameFormat.java index 854ec73eabe..4a174913a72 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/common/FeNameFormat.java +++ b/fe/fe-core/src/main/java/org/apache/doris/common/FeNameFormat.java @@ -29,24 +29,24 @@ import org.apache.doris.qe.VariableMgr; import com.google.common.base.Strings; public class FeNameFormat { - private static final String LABEL_REGEX = "^[-_A-Za-z0-9:]{1," + Config.label_regex_length + "}$"; + private static final String LABEL_REGEX = "^[\\-_A-Za-z0-9:]{1," + Config.label_regex_length + "}$"; // if modify the matching length of a regular expression, // please modify error msg when FeNameFormat.checkCommonName throw exception in CreateRoutineLoadStmt - private static final String COMMON_NAME_REGEX = "^[a-zA-Z][a-zA-Z0-9-_]{0,63}$"; - private static final String UNDERSCORE_COMMON_NAME_REGEX = "^[_a-zA-Z][a-zA-Z0-9-_]{0,63}$"; - private static final String TABLE_NAME_REGEX = "^[a-zA-Z][a-zA-Z0-9-_]*$"; - private static final String USER_NAME_REGEX = "^[a-zA-Z][a-zA-Z0-9.-_]*$"; - private static final String REPOSITORY_NAME_REGEX = "^[a-zA-Z][a-zA-Z0-9-_]{0,255}$"; - private static final String COLUMN_NAME_REGEX = "^[.a-zA-Z0-9_+-/?@#$%^&*\"\\s,:]{1,256}$"; - - private static final String UNICODE_LABEL_REGEX = "^[-_A-Za-z0-9:\\p{L}]{1," + Config.label_regex_length + "}$"; - private static final String UNICODE_COMMON_NAME_REGEX = "^[a-zA-Z\\p{L}][a-zA-Z0-9-_\\p{L}]{0,63}$"; - private static final String UNICODE_UNDERSCORE_COMMON_NAME_REGEX = "^[_a-zA-Z\\p{L}][a-zA-Z0-9-_\\p{L}]{0,63}$"; - private static final String UNICODE_TABLE_NAME_REGEX = "^[a-zA-Z\\p{L}][a-zA-Z0-9-_\\p{L}]*$"; - private static final String UNICODE_USER_NAME_REGEX = "^[a-zA-Z\\p{L}][a-zA-Z0-9.-_\\p{L}]*$"; + private static final String COMMON_NAME_REGEX = "^[a-zA-Z][a-zA-Z0-9\\-_]{0,63}$"; + private static final String UNDERSCORE_COMMON_NAME_REGEX = "^[_a-zA-Z][a-zA-Z0-9\\-_]{0,63}$"; + private static final String TABLE_NAME_REGEX = "^[a-zA-Z][a-zA-Z0-9\\-_]*$"; + private static final String USER_NAME_REGEX = "^[a-zA-Z][a-zA-Z0-9.\\-_]*$"; + private static final String REPOSITORY_NAME_REGEX = "^[a-zA-Z][a-zA-Z0-9\\-_]{0,255}$"; + private static final String COLUMN_NAME_REGEX = "^[.a-zA-Z0-9_+\\-/?@#$%^&*\"\\s,:]{1,256}$"; + + private static final String UNICODE_LABEL_REGEX = "^[\\-_A-Za-z0-9:\\p{L}]{1," + Config.label_regex_length + "}$"; + private static final String UNICODE_COMMON_NAME_REGEX = "^[a-zA-Z\\p{L}][a-zA-Z0-9\\-_\\p{L}]{0,63}$"; + private static final String UNICODE_UNDERSCORE_COMMON_NAME_REGEX = "^[_a-zA-Z\\p{L}][a-zA-Z0-9\\-_\\p{L}]{0,63}$"; + private static final String UNICODE_TABLE_NAME_REGEX = "^[a-zA-Z\\p{L}][a-zA-Z0-9\\-_\\p{L}]*$"; + private static final String UNICODE_USER_NAME_REGEX = "^[a-zA-Z\\p{L}][a-zA-Z0-9.\\-_\\p{L}]*$"; private static final String UNICODE_COLUMN_NAME_REGEX - = "^[.a-zA-Z0-9_+-/?@#$%^&*\"\\s,:\\p{L}]{1,256}$"; - private static final String UNICODE_REPOSITORY_NAME_REGEX = "^[a-zA-Z\\p{L}][a-zA-Z0-9-_\\p{L}]{0,255}$"; + = "^[.a-zA-Z0-9_+\\-/?@#$%^&*\"\\s,:\\p{L}]{1,256}$"; + private static final String UNICODE_REPOSITORY_NAME_REGEX = "^[a-zA-Z\\p{L}][a-zA-Z0-9\\-_\\p{L}]{0,255}$"; public static final String FORBIDDEN_PARTITION_NAME = "placeholder_"; diff --git a/fe/fe-core/src/test/java/org/apache/doris/common/FeNameFormatTest.java b/fe/fe-core/src/test/java/org/apache/doris/common/FeNameFormatTest.java index 32e2a553b94..e4a3b9c9f3c 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/common/FeNameFormatTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/common/FeNameFormatTest.java @@ -23,26 +23,103 @@ import com.google.common.collect.Lists; import org.apache.ivy.util.StringUtils; import org.junit.jupiter.api.Test; +import java.util.Arrays; import java.util.List; public class FeNameFormatTest { @Test void testLabelName() { - // check label use correct regex, begin with '-' is different from others - ExceptionChecker.expectThrowsNoException(() -> FeNameFormat.checkLabel("-lable")); + List<String> alwaysValid = Lists.newArrayList( + "abc123", // alphanumeric + "A-B_C:D", // contains all allowed special chars + "0-1_2:3", // starts with number, contains special chars + "a", // single character + "X-Y-Z", // hyphens and uppercase + "test_123:456", // mixed with underscore and colon + "-valid", // starts with hyphen + "_valid", // starts with underscore + ":valid", // starts with colon + StringUtils.repeat("a", Config.label_regex_length) // maximum length + ); + + List<String> alwaysInvalid = Lists.newArrayList( + "", // empty string + " ", // space character + "a b", // contains space + "a.b", // contains dot + "a@b", // contains @ + "a\nb", // contains newline + "a$b", // contains $ + "a*b", // contains * + "a#b", // contains # + StringUtils.repeat("a", Config.label_regex_length + 1) // maximum length + ); + + List<String> unicodeValid = Lists.newArrayList( + "äöü", // German umlauts + "北京", // Chinese characters + "東京123", // Japanese with numbers + "München", // German city name + "Beyoncé", // French name + "αβγ", // Greek letters + "русский", // Russian letters + "naïve", // French word + "Ḥello", // special diacritic + "øre", // Nordic word + "café", // French word + "ẞig" // German sharp S + ); + + test(FeNameFormat::checkLabel, alwaysValid, alwaysInvalid, unicodeValid); } @Test void testTableName() { - // length 64 - String tblName = "test_sys_partition_list_basic_test_list_partition_bigint_tb-uniq"; - ExceptionChecker.expectThrowsNoException(() -> FeNameFormat.checkTableName(tblName)); - // length 70 - String largeTblName = "test_sys_partition_list_basic_test_list_partition_bigint_tb_uniq_large"; - ExceptionChecker.expectThrows(AnalysisException.class, () -> FeNameFormat.checkTableName(largeTblName)); - // check table name use correct regex, not begin with '-' - ExceptionChecker.expectThrows(AnalysisException.class, () -> FeNameFormat.checkTableName("-" + tblName)); + List<String> alwaysValid = Lists.newArrayList( + "abc123", // Starts with ASCII letter, contains alphanumerics + underscores + "A_1_b", // Contains allowed symbols (underscore) + "Z", // Single ASCII letter + "a1b2c3", // Alphanumeric combination + "x_y_z", // Contains underscores + "test", // Letters only + "a_b_c", // Multiple underscores + "a_1", // Underscore + number + "B2" // Uppercase letter + number + ); + + List<String> alwaysInvalid = Lists.newArrayList( + "1abc", // Starts with digit + "@test", // Contains invalid symbol @ + "", // Empty string + "a b", // Contains space + "abc!", // Contains invalid symbol ! + "a\nb", // Contains newline + "abc$", // Contains invalid symbol $ + "-abc", // Starts with hyphen + "_abc", // Starts with underscore + "a*b", // Contains asterisk + "a.b", // Contains dot + "a#b" // Contains hash + ); + + List<String> unicodeValid = Lists.newArrayList( + "éclair", // Contains French letter + "über", // Contains German umlaut + "北京", // Chinese characters + "東京123", // Japanese characters + numbers + "München", // Contains umlaut + "Beyoncé", // Contains French accent + "αβγ", // Greek letters + "русский", // Cyrillic letters + "øre", // Nordic letter + "ção", // Portuguese letter + "naïve", // Contains diacritic + "Ḥello", // Contains special diacritic + "ẞig" // German sharp S + ); + + test(FeNameFormat::checkTableName, alwaysValid, alwaysInvalid, unicodeValid); } @Test @@ -92,22 +169,223 @@ public class FeNameFormatTest { "げんご" ); + test(FeNameFormat::checkColumnName, alwaysValid, alwaysInvalid, unicodeValid); + } + + @Test + void testUserName() { + List<String> alwaysValid = Arrays.asList( + "a", + "abc123", + "A-1_b.c", + "x.y-z_123", + "test", + "a.b-c_d", + "Z", + "a-", + "a_", + "a." + ); + List<String> alwaysInvalid = Arrays.asList( + "1abc", // starts with digit + "@test", // contains invalid character @ + "a b", // contains space + "", // empty string + "-abc", // starts with hyphen + ".abc", // starts with dot + "_abc", // starts with underscore + "abc!", // contains invalid character ! + "abc\n", // contains newline + "9", // digit only + " ", // whitespace only + "a\tb", // contains tab + "a\nb", // contains newline + "a*", // contains asterisk + "a(", // contains parenthesis + "a:b", // contains colon + " ab" // contains space + ); + List<String> unicodeValid = Lists.newArrayList( + "éclair", // starts with accented letter + "über", // starts with umlaut + "北京abc", // starts with Chinese characters + "東京123", // starts with Japanese kanji + "русский", // starts with Cyrillic letters + "αβγ.123", // starts with Greek letters + "München", // contains umlaut + "Beyoncé", // contains accented letter + "naïve" // contains diacritic + ); + test(FeNameFormat::checkUserName, alwaysValid, alwaysInvalid, unicodeValid); + } + + @Test + void testCommonName() { + List<String> alwaysValid = Arrays.asList( + "abc123", // ASCII letters + numbers + "A-1_b", // with allowed symbols (-_) + "Z", // single ASCII letter + "a1b2c3", // alphanumeric + "x_y-z", // underscore and hyphen + "test", // letters only + "a-b-c", // multiple hyphens + "a_b", // underscore + "a-1", // hyphen + number + "B2" // uppercase + number + ); + List<String> alwaysInvalid = Arrays.asList( + "1abc", // starts with number + "@test", // contains invalid symbol @ + "", // empty string + "a b", // contains space + "abc!", // contains invalid symbol ! + "a\nb", // contains newline + "abc$", // contains invalid symbol $ + "-abc", // starts with hyphen + "_abc", // starts with underscore + StringUtils.repeat("a", 65), // exceeds length limit (64) + "a*b", // contains asterisk + "a.b", // contains dot (if not allowed) + "a#b" // contains hash symbol + ); + List<String> unicodeValid = Lists.newArrayList( + "éclair", // French letters + "über", // German umlaut + "北京", // Chinese characters + "東京123", // Japanese + numbers + "München", // German umlaut + "Beyoncé", // French accent + "αβγ", // Greek letters + "русский", // Cyrillic letters + "øre", // Nordic letter + "ção", // Portuguese letter + "naïve", // French diacritic + "Ḥello", // special diacritic + "ẞig" // German sharp S + ); + test(FeNameFormatTest::checkCommonName, alwaysValid, alwaysInvalid, unicodeValid); + } + + @Test + void testOutfileName() { + List<String> alwaysValid = Arrays.asList( + "_valid", // starts with underscore + "a1_b-c", // letters, numbers, hyphens, underscores + "ValidName", // standard camel case + "x_123", // underscore + numbers + "A_B_C", // multiple underscores + "z", // single letter + "_9value", // starts with _, contains number + "a-b", // simple hyphenated + "MAX_LENGTH" // uppercase with underscores + ); + List<String> alwaysInvalid = Arrays.asList( + "1invalid", // starts with number + "@test", // invalid starting character + "", // empty string + "has space", // contains space + "a.b", // contains dot + "a*b", // contains asterisk + "a\nb", // contains newline + "-invalid", // starts with hyphen + "a#b", // contains hash + StringUtils.repeat("a", 65), // exceeds length limit (64) + "a>b", // contains angle bracket + "a$b" // contains dollar sign + ); + List<String> unicodeValid = Lists.newArrayList( + "_éclair", // starts with _, contains French letter + "über", // German umlaut + "_北京", // starts with _, Chinese characters + "東京123", // Japanese + numbers + "München", // German umlaut + "Beyoncé", // French accent + "αβγ", // Greek letters + "_русский", // starts with _, Cyrillic letters + "naïve", // French diacritic + "Ḥello", // special diacritic + "ẞig", // German sharp S + "øre", // Nordic letter + "_ção" // starts with _, Portuguese letter + ); + test(FeNameFormatTest::checkOutfileSuccessFileName, alwaysValid, alwaysInvalid, unicodeValid); + } + + @Test + void checkRepositoryName() { + List<String> alwaysValid = Arrays.asList( + "validName", // Standard ASCII letters + "A1_b2", // Letters, numbers, underscore + "Product123", // CamelCase with numbers + "x_y_z", // Multiple underscores + "test", // Lowercase only + "MAX_LENGTH", // Uppercase with underscores + "a1-b2", // Hyphen included + "Z", // Single character + StringUtils.repeat("a", 256) // Maximum length (255 chars) + ); + List<String> alwaysInvalid = Arrays.asList( + "", // Empty string + " ", // Space character + "1stInvalid", // Starts with number + "@username", // Invalid starting character + "name with space", // Contains space + "a.b", // Contains dot + "a*b", // Contains asterisk + "a\nb", // Contains newline + "a$b", // Contains dollar sign + "a#b", // Contains hash + StringUtils.repeat("a", 257), // Exceeds length limit (255) + "-invalid" // Starts with hyphen + ); + List<String> unicodeValid = Lists.newArrayList( + "München", // German umlaut + "Beyoncé", // French accent + "東京太郎", // Japanese characters + "北京123", // Chinese with numbers + "αβγ", // Greek letters + "русский", // Cyrillic letters + "naïve", // French diacritic + "øre", // Nordic letter + "café", // French word + "Ḥello", // Special diacritic + "ẞig", // German sharp S + "SãoPaulo" // Portuguese + ); + test(FeNameFormat::checkRepositoryName, alwaysValid, alwaysInvalid, unicodeValid); + } + + private static void checkOutfileSuccessFileName(String name) throws AnalysisException { + FeNameFormat.checkOutfileSuccessFileName("fakeType", name); + } + + private static void checkCommonName(String name) throws AnalysisException { + FeNameFormat.checkCommonName("fakeType", name); + } + + @FunctionalInterface + public interface NameValidator { + void accept(String name) throws AnalysisException; + } + + private void test(NameValidator validator, List<String> alwaysValid, List<String> alwaysInvalid, + List<String> unicodeValid) { boolean defaultUnicode = VariableMgr.getDefaultSessionVariable().enableUnicodeNameSupport; List<Boolean> enableUnicode = Lists.newArrayList(false, true); try { for (Boolean unicode : enableUnicode) { VariableMgr.getDefaultSessionVariable().setEnableUnicodeNameSupport(unicode); for (String s : alwaysValid) { - ExceptionChecker.expectThrowsNoException(() -> FeNameFormat.checkColumnName(s)); + ExceptionChecker.expectThrowsNoException(() -> validator.accept(s)); } for (String s : alwaysInvalid) { - ExceptionChecker.expectThrows(AnalysisException.class, () -> FeNameFormat.checkColumnName(s)); + ExceptionChecker.expectThrows(AnalysisException.class, () -> validator.accept(s)); } for (String s : unicodeValid) { if (unicode) { - ExceptionChecker.expectThrowsNoException(() -> FeNameFormat.checkColumnName(s)); + ExceptionChecker.expectThrowsNoException(() -> validator.accept(s)); } else { - ExceptionChecker.expectThrows(AnalysisException.class, () -> FeNameFormat.checkColumnName(s)); + ExceptionChecker.expectThrows(AnalysisException.class, () -> validator.accept(s)); } } } @@ -115,28 +393,4 @@ public class FeNameFormatTest { VariableMgr.getDefaultSessionVariable().setEnableUnicodeNameSupport(defaultUnicode); } } - - @Test - void testUserName() { - ExceptionChecker.expectThrowsNoException(() -> FeNameFormat.checkUserName("a.b")); - // check user name use correct regex, not begin with '.' - ExceptionChecker.expectThrows(AnalysisException.class, () -> FeNameFormat.checkUserName(".a.b")); - } - - @Test - void testCommonName() { - String commonName = "test_sys_partition_list_basic_test_list_partition_bigint_tb-uniq"; - - // check common name use correct regex, length 65 - ExceptionChecker.expectThrows(AnalysisException.class, () -> FeNameFormat.checkCommonName("fakeType", commonName + "t")); - ExceptionChecker.expectThrows(AnalysisException.class, () -> FeNameFormat.checkCommonName("fakeType", "_commonName")); - ExceptionChecker.expectThrowsNoException(() -> FeNameFormat.checkCommonName("fakeType", "common-Name")); - ExceptionChecker.expectThrowsNoException(() -> FeNameFormat.checkCommonName("fakeType", "commonName-")); - } - - @Test - void testOutfileName() { - // check success file name prefix - ExceptionChecker.expectThrowsNoException(() -> FeNameFormat.checkOutfileSuccessFileName("fakeType", "_success")); - } } --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org