[fix](auth) Authentication exception when the name of database or table contains an underscore in grant statement. (#10213)
This commit is contained in:
@ -20,27 +20,70 @@ package org.apache.doris.common;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
// Wrap for Java pattern and matcher
|
||||
public class PatternMatcher {
|
||||
public static final PatternMatcher MATCH_ANY = new PatternMatcher(Pattern.compile(".*"));
|
||||
private Pattern pattern;
|
||||
// The name of 'user', 'database' and 'table' don't support complex matching in grant statement.
|
||||
// Only using '%' to match any string. In other cases, it's string case-sensitive(or not) equivalent matching,
|
||||
// so using the origin string to determine whether it matches.
|
||||
private String originString;
|
||||
private boolean caseSensitive;
|
||||
|
||||
private static final Set<Character> FORBIDDEN_CHARS = Sets.newHashSet('<', '(', '[', '{', '^', '=',
|
||||
'$', '!', '|', ']', '}', ')',
|
||||
'?', '*', '+', '>', '@');
|
||||
|
||||
public PatternMatcher(Pattern pattern) {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
public PatternMatcher(String originString, boolean caseSensitive) {
|
||||
this.originString = caseSensitive ? originString : originString.toLowerCase(Locale.ROOT);
|
||||
this.caseSensitive = caseSensitive;
|
||||
}
|
||||
|
||||
public boolean match(String candidate) {
|
||||
if (pattern == null || candidate == null) {
|
||||
// No pattern, how can I explain this? Return false now.
|
||||
// No candidate, return false.
|
||||
if (candidate == null) {
|
||||
return false;
|
||||
}
|
||||
if (pattern.matcher(candidate).matches()) {
|
||||
return true;
|
||||
if (pattern != null) {
|
||||
return pattern.matcher(candidate).matches();
|
||||
}
|
||||
return false;
|
||||
if (caseSensitive) {
|
||||
return candidate.equals(originString);
|
||||
} else {
|
||||
return candidate.toLowerCase(Locale.ROOT).equals(originString);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use in grant statement to support case-sensitive(or not) equivalent matching.
|
||||
*
|
||||
* @param originString The string to match.
|
||||
* @param caseSensitive Case sensitive.
|
||||
*/
|
||||
public static PatternMatcher createFlatPattern(String originString, boolean caseSensitive) {
|
||||
return createFlatPattern(originString, caseSensitive, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use in grant statement to support case-sensitive(or not) equivalent matching, or arbitrary matching.
|
||||
*
|
||||
* @param originString The string to match. If matchAny = true, this parameter has no effect.
|
||||
* @param caseSensitive Case sensitive.
|
||||
* @param matchAny match any string.
|
||||
*/
|
||||
public static PatternMatcher createFlatPattern(
|
||||
String originString, boolean caseSensitive, boolean matchAny) {
|
||||
if (matchAny) {
|
||||
return MATCH_ANY;
|
||||
}
|
||||
return new PatternMatcher(originString, caseSensitive);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -149,7 +192,7 @@ public class PatternMatcher {
|
||||
|
||||
public static PatternMatcher createMysqlPattern(String mysqlPattern, boolean caseSensitive)
|
||||
throws AnalysisException {
|
||||
PatternMatcher matcher = new PatternMatcher();
|
||||
PatternMatcher matcher;
|
||||
|
||||
// Match nothing
|
||||
String newMysqlPattern = Strings.nullToEmpty(mysqlPattern);
|
||||
@ -157,9 +200,9 @@ public class PatternMatcher {
|
||||
String javaPattern = convertMysqlPattern(newMysqlPattern);
|
||||
try {
|
||||
if (caseSensitive) {
|
||||
matcher.pattern = Pattern.compile(javaPattern);
|
||||
matcher = new PatternMatcher(Pattern.compile(javaPattern));
|
||||
} else {
|
||||
matcher.pattern = Pattern.compile(javaPattern, Pattern.CASE_INSENSITIVE);
|
||||
matcher = new PatternMatcher(Pattern.compile(javaPattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new AnalysisException("Bad pattern in SQL: " + e.getMessage());
|
||||
|
||||
@ -54,7 +54,7 @@ public class DbPrivEntry extends PrivEntry {
|
||||
|
||||
PatternMatcher dbPattern = createDbPatternMatcher(db);
|
||||
|
||||
PatternMatcher userPattern = PatternMatcher.createMysqlPattern(user, CaseSensibility.USER.getCaseSensibility());
|
||||
PatternMatcher userPattern = PatternMatcher.createFlatPattern(user, CaseSensibility.USER.getCaseSensibility());
|
||||
|
||||
if (privs.containsNodePriv() || privs.containsResourcePriv()) {
|
||||
throw new AnalysisException("Db privilege can not contains global or resource privileges: " + privs);
|
||||
@ -70,8 +70,7 @@ public class DbPrivEntry extends PrivEntry {
|
||||
dbCaseSensibility = false;
|
||||
}
|
||||
|
||||
PatternMatcher dbPattern = PatternMatcher.createMysqlPattern(db.equals(ANY_DB) ? "%" : db, dbCaseSensibility);
|
||||
return dbPattern;
|
||||
return PatternMatcher.createFlatPattern(db, dbCaseSensibility, db.equals(ANY_DB));
|
||||
}
|
||||
|
||||
public PatternMatcher getDbPattern() {
|
||||
|
||||
@ -53,7 +53,7 @@ public class GlobalPrivEntry extends PrivEntry {
|
||||
public static GlobalPrivEntry create(String host, String user, boolean isDomain, byte[] password, PrivBitSet privs)
|
||||
throws AnalysisException {
|
||||
PatternMatcher hostPattern = PatternMatcher.createMysqlPattern(host, CaseSensibility.HOST.getCaseSensibility());
|
||||
PatternMatcher userPattern = PatternMatcher.createMysqlPattern(user, CaseSensibility.USER.getCaseSensibility());
|
||||
PatternMatcher userPattern = PatternMatcher.createFlatPattern(user, CaseSensibility.USER.getCaseSensibility());
|
||||
return new GlobalPrivEntry(hostPattern, host, userPattern, user, isDomain, password, privs);
|
||||
}
|
||||
|
||||
|
||||
@ -47,7 +47,7 @@ public class PrivBitSet implements Writable {
|
||||
|
||||
public void unset(int index) {
|
||||
Preconditions.checkState(index < PaloPrivilege.privileges.length, index);
|
||||
set &= ~set;
|
||||
set &= 1 << index;
|
||||
}
|
||||
|
||||
public boolean get(int index) {
|
||||
|
||||
@ -50,12 +50,12 @@ public class TablePrivEntry extends DbPrivEntry {
|
||||
public static TablePrivEntry create(String host, String db, String user, String tbl, boolean isDomain,
|
||||
PrivBitSet privs) throws AnalysisException {
|
||||
PatternMatcher hostPattern = PatternMatcher.createMysqlPattern(host, CaseSensibility.HOST.getCaseSensibility());
|
||||
PatternMatcher dbPattern = PatternMatcher.createMysqlPattern(db.equals(ANY_DB) ? "%" : db,
|
||||
CaseSensibility.DATABASE.getCaseSensibility());
|
||||
PatternMatcher userPattern = PatternMatcher.createMysqlPattern(user, CaseSensibility.USER.getCaseSensibility());
|
||||
PatternMatcher dbPattern = PatternMatcher.createFlatPattern(
|
||||
db, CaseSensibility.DATABASE.getCaseSensibility(), db.equals(ANY_DB));
|
||||
PatternMatcher userPattern = PatternMatcher.createFlatPattern(user, CaseSensibility.USER.getCaseSensibility());
|
||||
|
||||
PatternMatcher tblPattern = PatternMatcher.createMysqlPattern(tbl.equals(ANY_TBL) ? "%" : tbl,
|
||||
CaseSensibility.TABLE.getCaseSensibility());
|
||||
PatternMatcher tblPattern = PatternMatcher.createFlatPattern(
|
||||
tbl, CaseSensibility.TABLE.getCaseSensibility(), tbl.equals(ANY_TBL));
|
||||
|
||||
if (privs.containsNodePriv() || privs.containsResourcePriv()) {
|
||||
throw new AnalysisException("Table privilege can not contains global or resource privileges: " + privs);
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.doris.mysql.privilege;
|
||||
|
||||
import org.apache.doris.analysis.UserIdentity;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class PrivEntryTest {
|
||||
@Test
|
||||
public void testNameWithUnderscores() throws Exception {
|
||||
TablePrivEntry tablePrivEntry = TablePrivEntry.create(
|
||||
"127.%", "db_db1", "user1", "tbl_tbl1", false,
|
||||
PrivBitSet.of(PaloPrivilege.SELECT_PRIV, PaloPrivilege.DROP_PRIV));
|
||||
// pattern match
|
||||
Assert.assertFalse(tablePrivEntry.getDbPattern().match("db-db1"));
|
||||
Assert.assertFalse(tablePrivEntry.getTblPattern().match("tbl-tbl1"));
|
||||
// create TablePrivTable
|
||||
TablePrivTable tablePrivTable = new TablePrivTable();
|
||||
tablePrivTable.addEntry(tablePrivEntry, false, false);
|
||||
UserIdentity userIdentity = new UserIdentity("user1", "127.%", false);
|
||||
userIdentity.setIsAnalyzed();
|
||||
|
||||
PrivBitSet privs1 = PrivBitSet.of();
|
||||
tablePrivTable.getPrivs(userIdentity, "db#db1", "tbl#tbl1", privs1);
|
||||
Assert.assertFalse(PaloPrivilege.satisfy(privs1, PrivPredicate.DROP));
|
||||
|
||||
PrivBitSet privs2 = PrivBitSet.of();
|
||||
tablePrivTable.getPrivs(userIdentity, "db_db1", "tbl_tbl1", privs2);
|
||||
Assert.assertTrue(PaloPrivilege.satisfy(privs2, PrivPredicate.DROP));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user