[fix](auth) Authentication exception when the name of database or table contains an underscore in grant statement. (#10213)

This commit is contained in:
Ashin Gau
2022-06-19 22:20:01 +08:00
committed by GitHub
parent 67f341f44e
commit f728fd4933
6 changed files with 109 additions and 19 deletions

View File

@ -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());

View File

@ -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() {

View File

@ -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);
}

View File

@ -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) {

View File

@ -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);

View File

@ -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));
}
}