[feature-wip](multi-catalog)(resubmit) add catalog level privileges (#10345)
This commit is contained in:
@ -1365,10 +1365,13 @@ public class Analyzer {
|
||||
* is already fully qualified, returns tableName.
|
||||
*/
|
||||
public TableName getFqTableName(TableName tableName) {
|
||||
if (tableName.isFullyQualified()) {
|
||||
return tableName;
|
||||
if (Strings.isNullOrEmpty(tableName.getCtl())) {
|
||||
tableName.setCtl(getDefaultCatalog());
|
||||
}
|
||||
return new TableName(getDefaultDb(), tableName.getTbl());
|
||||
if (Strings.isNullOrEmpty(tableName.getDb())) {
|
||||
tableName.setDb(getDefaultDb());
|
||||
}
|
||||
return tableName;
|
||||
}
|
||||
|
||||
public TupleId getTupleId(SlotId slotId) {
|
||||
@ -1935,6 +1938,10 @@ public class Analyzer {
|
||||
return globalState.context.getConnectionId();
|
||||
}
|
||||
|
||||
public String getDefaultCatalog() {
|
||||
return globalState.context.getDefaultCatalog();
|
||||
}
|
||||
|
||||
public String getDefaultDb() {
|
||||
return globalState.context.getDatabase();
|
||||
}
|
||||
|
||||
@ -107,7 +107,7 @@ public class GrantStmt extends DdlStmt {
|
||||
}
|
||||
|
||||
if (tblPattern != null) {
|
||||
tblPattern.analyze(analyzer.getClusterName());
|
||||
tblPattern.analyze(analyzer);
|
||||
} else {
|
||||
// TODO(wyb): spark-load
|
||||
if (!Config.enable_spark_load) {
|
||||
@ -148,7 +148,7 @@ public class GrantStmt extends DdlStmt {
|
||||
// Rule 1
|
||||
if (tblPattern.getPrivLevel() != PrivLevel.GLOBAL && (privileges.contains(PaloPrivilege.ADMIN_PRIV)
|
||||
|| privileges.contains(PaloPrivilege.NODE_PRIV))) {
|
||||
throw new AnalysisException("ADMIN_PRIV and NODE_PRIV can only be granted on *.*");
|
||||
throw new AnalysisException("ADMIN_PRIV and NODE_PRIV can only be granted on *.*.*");
|
||||
}
|
||||
|
||||
// Rule 2
|
||||
|
||||
@ -97,7 +97,7 @@ public class RevokeStmt extends DdlStmt {
|
||||
}
|
||||
|
||||
if (tblPattern != null) {
|
||||
tblPattern.analyze(analyzer.getClusterName());
|
||||
tblPattern.analyze(analyzer);
|
||||
} else {
|
||||
// TODO(wyb): spark-load
|
||||
if (!Config.enable_spark_load) {
|
||||
|
||||
@ -36,6 +36,7 @@ public class ShowRolesStmt extends ShowStmt {
|
||||
builder.addColumn(new Column("Name", ScalarType.createVarchar(100)));
|
||||
builder.addColumn(new Column("Users", ScalarType.createVarchar(100)));
|
||||
builder.addColumn(new Column("GlobalPrivs", ScalarType.createVarchar(300)));
|
||||
builder.addColumn(new Column("CatalogPrivs", ScalarType.createVarchar(300)));
|
||||
builder.addColumn(new Column("DatabasePrivs", ScalarType.createVarchar(300)));
|
||||
builder.addColumn(new Column("TablePrivs", ScalarType.createVarchar(300)));
|
||||
builder.addColumn(new Column("ResourcePrivs", ScalarType.createVarchar(300)));
|
||||
|
||||
@ -25,32 +25,57 @@ import org.apache.doris.cluster.ClusterNamespace;
|
||||
import org.apache.doris.common.AnalysisException;
|
||||
import org.apache.doris.common.ErrorCode;
|
||||
import org.apache.doris.common.ErrorReport;
|
||||
import org.apache.doris.common.FeMetaVersion;
|
||||
import org.apache.doris.common.io.Text;
|
||||
import org.apache.doris.common.io.Writable;
|
||||
import org.apache.doris.datasource.InternalDataSource;
|
||||
import org.apache.doris.persist.gson.GsonUtils;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class TableName implements Writable {
|
||||
@SerializedName(value = "ctl")
|
||||
private String ctl;
|
||||
@SerializedName(value = "tbl")
|
||||
private String tbl;
|
||||
@SerializedName(value = "db")
|
||||
private String db;
|
||||
|
||||
public TableName() {
|
||||
|
||||
}
|
||||
|
||||
public TableName(String db, String tbl) {
|
||||
public TableName(String ctl, String db, String tbl) {
|
||||
if (Catalog.isStoredTableNamesLowerCase() && !Strings.isNullOrEmpty(tbl)) {
|
||||
tbl = tbl.toLowerCase();
|
||||
}
|
||||
this.ctl = ctl;
|
||||
this.db = db;
|
||||
this.tbl = tbl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize catalog in analyze.
|
||||
*/
|
||||
public TableName(String db, String tbl) {
|
||||
this(null, db, tbl);
|
||||
}
|
||||
|
||||
public void analyze(Analyzer analyzer) throws AnalysisException {
|
||||
if (Strings.isNullOrEmpty(ctl)) {
|
||||
ctl = analyzer.getDefaultCatalog();
|
||||
if (Strings.isNullOrEmpty(ctl)) {
|
||||
ctl = InternalDataSource.INTERNAL_DS_NAME;
|
||||
}
|
||||
}
|
||||
if (Strings.isNullOrEmpty(db)) {
|
||||
db = analyzer.getDefaultDb();
|
||||
if (Strings.isNullOrEmpty(db)) {
|
||||
@ -68,6 +93,14 @@ public class TableName implements Writable {
|
||||
}
|
||||
}
|
||||
|
||||
public String getCtl() {
|
||||
return ctl;
|
||||
}
|
||||
|
||||
public void setCtl(String ctl) {
|
||||
this.ctl = ctl;
|
||||
}
|
||||
|
||||
public String getDb() {
|
||||
return db;
|
||||
}
|
||||
@ -85,33 +118,30 @@ public class TableName implements Writable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this name has a non-empty database field and a non-empty
|
||||
* table name.
|
||||
* Returns true if this name has a non-empty catalog and a non-empty database field
|
||||
* and a non-empty table name.
|
||||
*/
|
||||
public boolean isFullyQualified() {
|
||||
return db != null && !db.isEmpty() && !tbl.isEmpty();
|
||||
return Stream.of(ctl, db, tbl).noneMatch(Strings::isNullOrEmpty);
|
||||
}
|
||||
|
||||
public String getNoClusterString() {
|
||||
if (db == null) {
|
||||
return tbl;
|
||||
} else {
|
||||
String dbName = ClusterNamespace.getNameFromFullName(db);
|
||||
if (dbName == null) {
|
||||
return db + "." + tbl;
|
||||
} else {
|
||||
return dbName + "." + tbl;
|
||||
}
|
||||
}
|
||||
return Stream.of(ctl, ClusterNamespace.getNameFromFullName(db), tbl)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.joining("."));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (db == null) {
|
||||
return tbl;
|
||||
} else {
|
||||
return db + "." + tbl;
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
if (ctl != null && !ctl.equals(InternalDataSource.INTERNAL_DS_NAME)) {
|
||||
stringBuilder.append(ctl).append(".");
|
||||
}
|
||||
if (db != null) {
|
||||
stringBuilder.append(db).append(".");
|
||||
}
|
||||
stringBuilder.append(tbl);
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -127,6 +157,9 @@ public class TableName implements Writable {
|
||||
|
||||
public String toSql() {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
if (ctl != null && !ctl.equals(InternalDataSource.INTERNAL_DS_NAME)) {
|
||||
stringBuilder.append("`").append(ctl).append("`.");
|
||||
}
|
||||
if (db != null) {
|
||||
stringBuilder.append("`").append(db).append("`.");
|
||||
}
|
||||
@ -136,17 +169,24 @@ public class TableName implements Writable {
|
||||
|
||||
@Override
|
||||
public void write(DataOutput out) throws IOException {
|
||||
Text.writeString(out, db);
|
||||
Text.writeString(out, tbl);
|
||||
String json = GsonUtils.GSON.toJson(this);
|
||||
Text.writeString(out, json);
|
||||
}
|
||||
|
||||
public void readFields(DataInput in) throws IOException {
|
||||
db = Text.readString(in);
|
||||
tbl = Text.readString(in);
|
||||
if (Catalog.getCurrentCatalogJournalVersion() >= FeMetaVersion.VERSION_111) {
|
||||
TableName fromJson = GsonUtils.GSON.fromJson(Text.readString(in), TableName.class);
|
||||
ctl = fromJson.ctl;
|
||||
db = fromJson.db;
|
||||
tbl = fromJson.tbl;
|
||||
} else {
|
||||
ctl = InternalDataSource.INTERNAL_DS_NAME;
|
||||
db = Text.readString(in);
|
||||
tbl = Text.readString(in);
|
||||
}
|
||||
}
|
||||
|
||||
public TableName cloneWithoutAnalyze() {
|
||||
TableName tableName = new TableName(this.db, this.tbl);
|
||||
return tableName;
|
||||
return new TableName(this.ctl, this.db, this.tbl);
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,33 +17,46 @@
|
||||
|
||||
package org.apache.doris.analysis;
|
||||
|
||||
import org.apache.doris.catalog.Catalog;
|
||||
import org.apache.doris.cluster.ClusterNamespace;
|
||||
import org.apache.doris.common.AnalysisException;
|
||||
import org.apache.doris.common.FeMetaVersion;
|
||||
import org.apache.doris.common.FeNameFormat;
|
||||
import org.apache.doris.common.io.Text;
|
||||
import org.apache.doris.common.io.Writable;
|
||||
import org.apache.doris.datasource.InternalDataSource;
|
||||
import org.apache.doris.mysql.privilege.PaloAuth.PrivLevel;
|
||||
import org.apache.doris.persist.gson.GsonUtils;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
// only the following 3 formats are allowed
|
||||
// db.tbl
|
||||
// *.*
|
||||
// db.*
|
||||
/**
|
||||
* Three-segment-format: catalog.database.table. If the lower segment is specific,
|
||||
* the higher segment can't be a wildcard. The following examples are not allowed:
|
||||
* "ctl1.*.table1", "*.*.table2", "*.db1.*", ...
|
||||
*/
|
||||
public class TablePattern implements Writable {
|
||||
@SerializedName(value = "ctl")
|
||||
private String ctl;
|
||||
@SerializedName(value = "db")
|
||||
private String db;
|
||||
@SerializedName(value = "tbl")
|
||||
private String tbl;
|
||||
boolean isAnalyzed = false;
|
||||
|
||||
public static TablePattern ALL;
|
||||
|
||||
static {
|
||||
ALL = new TablePattern("*", "*");
|
||||
ALL = new TablePattern("*", "*", "*");
|
||||
try {
|
||||
ALL.analyze("");
|
||||
} catch (AnalysisException e) {
|
||||
@ -54,11 +67,23 @@ public class TablePattern implements Writable {
|
||||
private TablePattern() {
|
||||
}
|
||||
|
||||
public TablePattern(String db, String tbl) {
|
||||
public TablePattern(String ctl, String db, String tbl) {
|
||||
this.ctl = Strings.isNullOrEmpty(ctl) ? "*" : ctl;
|
||||
this.db = Strings.isNullOrEmpty(db) ? "*" : db;
|
||||
this.tbl = Strings.isNullOrEmpty(tbl) ? "*" : tbl;
|
||||
}
|
||||
|
||||
public TablePattern(String db, String tbl) {
|
||||
this.ctl = null;
|
||||
this.db = Strings.isNullOrEmpty(db) ? "*" : db;
|
||||
this.tbl = Strings.isNullOrEmpty(tbl) ? "*" : tbl;
|
||||
}
|
||||
|
||||
public String getQualifiedCtl() {
|
||||
Preconditions.checkState(isAnalyzed);
|
||||
return ctl;
|
||||
}
|
||||
|
||||
public String getQualifiedDb() {
|
||||
Preconditions.checkState(isAnalyzed);
|
||||
return db;
|
||||
@ -70,23 +95,39 @@ public class TablePattern implements Writable {
|
||||
|
||||
public PrivLevel getPrivLevel() {
|
||||
Preconditions.checkState(isAnalyzed);
|
||||
if (db.equals("*")) {
|
||||
if (ctl.equals("*")) {
|
||||
return PrivLevel.GLOBAL;
|
||||
} else if (!tbl.equals("*")) {
|
||||
return PrivLevel.TABLE;
|
||||
} else {
|
||||
} else if (db.equals("*")) {
|
||||
return PrivLevel.CATALOG;
|
||||
} else if (tbl.equals("*")) {
|
||||
return PrivLevel.DATABASE;
|
||||
} else {
|
||||
return PrivLevel.TABLE;
|
||||
}
|
||||
}
|
||||
|
||||
public void analyze(String clusterName) throws AnalysisException {
|
||||
public void analyze(Analyzer analyzer) throws AnalysisException {
|
||||
if (ctl == null) {
|
||||
analyze(analyzer.getDefaultCatalog(), analyzer.getClusterName());
|
||||
} else {
|
||||
analyze(analyzer.getClusterName());
|
||||
}
|
||||
}
|
||||
|
||||
private void analyze(String catalogName, String clusterName) throws AnalysisException {
|
||||
if (isAnalyzed) {
|
||||
return;
|
||||
}
|
||||
if (db.equals("*") && !tbl.equals("*")) {
|
||||
this.ctl = Strings.isNullOrEmpty(catalogName) ? InternalDataSource.INTERNAL_DS_NAME : catalogName;
|
||||
if ((!tbl.equals("*") && (db.equals("*") || ctl.equals("*")))
|
||||
|| (!db.equals("*") && ctl.equals("*"))) {
|
||||
throw new AnalysisException("Do not support format: " + toString());
|
||||
}
|
||||
|
||||
if (!ctl.equals("*")) {
|
||||
FeNameFormat.checkCatalogName(ctl);
|
||||
}
|
||||
|
||||
if (!db.equals("*")) {
|
||||
FeNameFormat.checkDbName(db);
|
||||
db = ClusterNamespace.getFullName(clusterName, db);
|
||||
@ -98,9 +139,21 @@ public class TablePattern implements Writable {
|
||||
isAnalyzed = true;
|
||||
}
|
||||
|
||||
public void analyze(String clusterName) throws AnalysisException {
|
||||
analyze(ctl, clusterName);
|
||||
}
|
||||
|
||||
public static TablePattern read(DataInput in) throws IOException {
|
||||
TablePattern tablePattern = new TablePattern();
|
||||
tablePattern.readFields(in);
|
||||
TablePattern tablePattern;
|
||||
if (Catalog.getCurrentCatalogJournalVersion() >= FeMetaVersion.VERSION_111) {
|
||||
tablePattern = GsonUtils.GSON.fromJson(Text.readString(in), TablePattern.class);
|
||||
} else {
|
||||
String ctl = InternalDataSource.INTERNAL_DS_NAME;
|
||||
String db = Text.readString(in);
|
||||
String tbl = Text.readString(in);
|
||||
tablePattern = new TablePattern(ctl, db, tbl);
|
||||
}
|
||||
tablePattern.isAnalyzed = true;
|
||||
return tablePattern;
|
||||
}
|
||||
|
||||
@ -110,34 +163,25 @@ public class TablePattern implements Writable {
|
||||
return false;
|
||||
}
|
||||
TablePattern other = (TablePattern) obj;
|
||||
return db.equals(other.getQualifiedDb()) && tbl.equals(other.getTbl());
|
||||
return ctl.equals(other.getQualifiedCtl()) && db.equals(other.getQualifiedDb()) && tbl.equals(other.getTbl());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 17;
|
||||
result = 31 * result + db.hashCode();
|
||||
result = 31 * result + tbl.hashCode();
|
||||
return result;
|
||||
return Stream.of(ctl, db, tbl).filter(Objects::nonNull)
|
||||
.map(String::hashCode)
|
||||
.reduce(17, (acc, h) -> 31 * acc + h);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(db).append(".").append(tbl);
|
||||
return sb.toString();
|
||||
return Stream.of(ctl, db, tbl).filter(Objects::nonNull).collect(Collectors.joining("."));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput out) throws IOException {
|
||||
Preconditions.checkState(isAnalyzed);
|
||||
Text.writeString(out, db);
|
||||
Text.writeString(out, tbl);
|
||||
}
|
||||
|
||||
public void readFields(DataInput in) throws IOException {
|
||||
db = Text.readString(in);
|
||||
tbl = Text.readString(in);
|
||||
isAnalyzed = true;
|
||||
String json = GsonUtils.GSON.toJson(this);
|
||||
Text.writeString(out, json);
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,6 +22,7 @@ package org.apache.doris.common;
|
||||
**/
|
||||
public enum CaseSensibility {
|
||||
CLUSTER(true),
|
||||
CATALOG(true),
|
||||
DATABASE(true),
|
||||
TABLE(true),
|
||||
ROLLUP(true),
|
||||
|
||||
@ -1686,7 +1686,7 @@ public enum ErrorCode {
|
||||
+ "Use `SHOW PARTITIONS FROM %s` to see the currently partitions of this table. "),
|
||||
ERROR_SQL_AND_LIMITATIONS_SET_IN_ONE_RULE(5084, new byte[]{'4', '2', '0', '0', '0'},
|
||||
"sql/sqlHash and partition_num/tablet_num/cardinality cannot be set in one rule."),
|
||||
;
|
||||
ERR_WRONG_CATALOG_NAME(5085, new byte[]{'4', '2', '0', '0', '0'}, "Incorrect catalog name '%s'");
|
||||
|
||||
// This is error code
|
||||
private final int code;
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
package org.apache.doris.common;
|
||||
|
||||
import org.apache.doris.alter.SchemaChangeHandler;
|
||||
import org.apache.doris.datasource.InternalDataSource;
|
||||
import org.apache.doris.mysql.privilege.PaloRole;
|
||||
import org.apache.doris.system.SystemInfoService;
|
||||
|
||||
@ -42,6 +43,13 @@ public class FeNameFormat {
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkCatalogName(String catalogName) throws AnalysisException {
|
||||
if (!InternalDataSource.INTERNAL_DS_NAME.equals(catalogName)
|
||||
&& (Strings.isNullOrEmpty(catalogName) || !catalogName.matches(COMMON_NAME_REGEX))) {
|
||||
ErrorReport.reportAnalysisException(ErrorCode.ERR_WRONG_CATALOG_NAME, catalogName);
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkDbName(String dbName) throws AnalysisException {
|
||||
if (Strings.isNullOrEmpty(dbName) || !dbName.matches(COMMON_NAME_REGEX)) {
|
||||
ErrorReport.reportAnalysisException(ErrorCode.ERR_WRONG_DB_NAME, dbName);
|
||||
|
||||
@ -31,8 +31,8 @@ import com.google.common.collect.ImmutableList;
|
||||
*/
|
||||
public class AuthProcDir implements ProcDirInterface {
|
||||
public static final ImmutableList<String> TITLE_NAMES = new ImmutableList.Builder<String>()
|
||||
.add("UserIdentity").add("Password").add("GlobalPrivs").add("DatabasePrivs")
|
||||
.add("TablePrivs").add("ResourcePrivs").build();
|
||||
.add("UserIdentity").add("Password").add("GlobalPrivs").add("CatalogPrivs")
|
||||
.add("DatabasePrivs").add("TablePrivs").add("ResourcePrivs").build();
|
||||
|
||||
private PaloAuth auth;
|
||||
|
||||
|
||||
@ -0,0 +1,143 @@
|
||||
// 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.catalog.Catalog;
|
||||
import org.apache.doris.common.AnalysisException;
|
||||
import org.apache.doris.common.CaseSensibility;
|
||||
import org.apache.doris.common.FeMetaVersion;
|
||||
import org.apache.doris.common.PatternMatcher;
|
||||
import org.apache.doris.common.io.Text;
|
||||
import org.apache.doris.datasource.InternalDataSource;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
public class CatalogPrivEntry extends PrivEntry {
|
||||
protected static final String ANY_CTL = "*";
|
||||
|
||||
protected PatternMatcher ctlPattern;
|
||||
protected String origCtl;
|
||||
protected boolean isAnyCtl;
|
||||
|
||||
protected CatalogPrivEntry() {
|
||||
}
|
||||
|
||||
protected CatalogPrivEntry(PatternMatcher userPattern, String user,
|
||||
PatternMatcher hostPattern, String origHost,
|
||||
PatternMatcher ctlPattern, String origCtl,
|
||||
boolean isDomain, PrivBitSet privSet) {
|
||||
super(hostPattern, origHost, userPattern, user, isDomain, privSet);
|
||||
this.ctlPattern = ctlPattern;
|
||||
this.origCtl = origCtl;
|
||||
if (origCtl.equals(ANY_CTL)) {
|
||||
isAnyCtl = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static CatalogPrivEntry create(String user, String host, String ctl, boolean isDomain, PrivBitSet privs)
|
||||
throws AnalysisException {
|
||||
PatternMatcher hostPattern = PatternMatcher.createMysqlPattern(host, CaseSensibility.HOST.getCaseSensibility());
|
||||
|
||||
PatternMatcher ctlPattern = createCtlPatternMatcher(ctl);
|
||||
|
||||
PatternMatcher userPattern = PatternMatcher.createFlatPattern(user, CaseSensibility.USER.getCaseSensibility());
|
||||
|
||||
if (privs.containsNodePriv() || privs.containsResourcePriv()) {
|
||||
throw new AnalysisException("Datasource privilege can not contains node or resource privileges: " + privs);
|
||||
}
|
||||
|
||||
return new CatalogPrivEntry(userPattern, user, hostPattern, host, ctlPattern, ctl, isDomain, privs);
|
||||
}
|
||||
|
||||
private static PatternMatcher createCtlPatternMatcher(String ctl) throws AnalysisException {
|
||||
boolean ctlCaseSensibility = CaseSensibility.CATALOG.getCaseSensibility();
|
||||
return PatternMatcher.createFlatPattern(ctl, ctlCaseSensibility, ctl.equals(ANY_CTL));
|
||||
}
|
||||
|
||||
public PatternMatcher getCtlPattern() {
|
||||
return ctlPattern;
|
||||
}
|
||||
|
||||
public String getOrigCtl() {
|
||||
return origCtl;
|
||||
}
|
||||
|
||||
public boolean isAnyCtl() {
|
||||
return isAnyCtl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(PrivEntry other) {
|
||||
if (!(other instanceof CatalogPrivEntry)) {
|
||||
throw new ClassCastException("cannot cast " + other.getClass().toString() + " to " + this.getClass());
|
||||
}
|
||||
|
||||
CatalogPrivEntry otherEntry = (CatalogPrivEntry) other;
|
||||
return compareAssist(origUser, otherEntry.origUser,
|
||||
origHost, otherEntry.origHost,
|
||||
origCtl, otherEntry.origCtl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyMatch(PrivEntry other) {
|
||||
if (!(other instanceof CatalogPrivEntry)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CatalogPrivEntry otherEntry = (CatalogPrivEntry) other;
|
||||
return origUser.equals(otherEntry.origUser) && origHost.equals(otherEntry.origHost)
|
||||
&& origCtl.equals(otherEntry.origCtl) && isDomain == otherEntry.isDomain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("catalog privilege. user: %s, host: %s, ctl: %s, priv: %s, set by resolver: %b",
|
||||
origUser, origHost, origCtl, privSet.toString(), isSetByDomainResolver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput out) throws IOException {
|
||||
if (!isClassNameWrote) {
|
||||
String className = CatalogPrivEntry.class.getCanonicalName();
|
||||
Text.writeString(out, className);
|
||||
isClassNameWrote = true;
|
||||
}
|
||||
super.write(out);
|
||||
Text.writeString(out, origCtl);
|
||||
isClassNameWrote = false;
|
||||
}
|
||||
|
||||
public void readFields(DataInput in) throws IOException {
|
||||
super.readFields(in);
|
||||
|
||||
if (Catalog.getCurrentCatalogJournalVersion() >= FeMetaVersion.VERSION_111) {
|
||||
origCtl = Text.readString(in);
|
||||
} else {
|
||||
origCtl = InternalDataSource.INTERNAL_DS_NAME;
|
||||
}
|
||||
try {
|
||||
ctlPattern = createCtlPatternMatcher(origCtl);
|
||||
} catch (AnalysisException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
isAnyCtl = origCtl.equals(ANY_CTL);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
// 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.apache.doris.common.io.Text;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
/*
|
||||
* CatalogPrivTable saves all catalog level privs
|
||||
*/
|
||||
public class CatalogPrivTable extends PrivTable {
|
||||
private static final Logger LOG = LogManager.getLogger(CatalogPrivTable.class);
|
||||
|
||||
/*
|
||||
* Return first priv which match the user@host on ctl.* The returned priv will be
|
||||
* saved in 'savedPrivs'.
|
||||
*/
|
||||
public void getPrivs(UserIdentity currentUser, String ctl, PrivBitSet savedPrivs) {
|
||||
CatalogPrivEntry matchedEntry = null;
|
||||
for (PrivEntry entry : entries) {
|
||||
CatalogPrivEntry dsPrivEntry = (CatalogPrivEntry) entry;
|
||||
|
||||
if (!dsPrivEntry.match(currentUser, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check catalog
|
||||
if (!dsPrivEntry.isAnyCtl() && !dsPrivEntry.getCtlPattern().match(ctl)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
matchedEntry = dsPrivEntry;
|
||||
break;
|
||||
}
|
||||
if (matchedEntry == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
savedPrivs.or(matchedEntry.getPrivSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput out) throws IOException {
|
||||
if (!isClassNameWrote) {
|
||||
String className = CatalogPrivTable.class.getCanonicalName();
|
||||
Text.writeString(out, className);
|
||||
isClassNameWrote = true;
|
||||
}
|
||||
|
||||
super.write(out);
|
||||
}
|
||||
}
|
||||
@ -28,7 +28,7 @@ import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
public class DbPrivEntry extends PrivEntry {
|
||||
public class DbPrivEntry extends CatalogPrivEntry {
|
||||
protected static final String ANY_DB = "*";
|
||||
|
||||
protected PatternMatcher dbPattern;
|
||||
@ -38,9 +38,12 @@ public class DbPrivEntry extends PrivEntry {
|
||||
protected DbPrivEntry() {
|
||||
}
|
||||
|
||||
protected DbPrivEntry(PatternMatcher hostPattern, String origHost, PatternMatcher dbPattern, String origDb,
|
||||
PatternMatcher userPattern, String user, boolean isDomain, PrivBitSet privSet) {
|
||||
super(hostPattern, origHost, userPattern, user, isDomain, privSet);
|
||||
protected DbPrivEntry(PatternMatcher userPattern, String user,
|
||||
PatternMatcher hostPattern, String origHost,
|
||||
PatternMatcher ctlPattern, String origCtl,
|
||||
PatternMatcher dbPattern, String origDb,
|
||||
boolean isDomain, PrivBitSet privSet) {
|
||||
super(userPattern, user, hostPattern, origHost, ctlPattern, origCtl, isDomain, privSet);
|
||||
this.dbPattern = dbPattern;
|
||||
this.origDb = origDb;
|
||||
if (origDb.equals(ANY_DB)) {
|
||||
@ -48,10 +51,15 @@ public class DbPrivEntry extends PrivEntry {
|
||||
}
|
||||
}
|
||||
|
||||
public static DbPrivEntry create(String host, String db, String user, boolean isDomain, PrivBitSet privs)
|
||||
throws AnalysisException {
|
||||
public static DbPrivEntry create(
|
||||
String user, String host,
|
||||
String ctl, String db,
|
||||
boolean isDomain, PrivBitSet privs) throws AnalysisException {
|
||||
PatternMatcher hostPattern = PatternMatcher.createMysqlPattern(host, CaseSensibility.HOST.getCaseSensibility());
|
||||
|
||||
PatternMatcher ctlPattern = PatternMatcher.createFlatPattern(
|
||||
ctl, CaseSensibility.CATALOG.getCaseSensibility(), ctl.equals(ANY_CTL));
|
||||
|
||||
PatternMatcher dbPattern = createDbPatternMatcher(db);
|
||||
|
||||
PatternMatcher userPattern = PatternMatcher.createFlatPattern(user, CaseSensibility.USER.getCaseSensibility());
|
||||
@ -60,7 +68,7 @@ public class DbPrivEntry extends PrivEntry {
|
||||
throw new AnalysisException("Db privilege can not contains global or resource privileges: " + privs);
|
||||
}
|
||||
|
||||
return new DbPrivEntry(hostPattern, host, dbPattern, db, userPattern, user, isDomain, privs);
|
||||
return new DbPrivEntry(userPattern, user, hostPattern, host, ctlPattern, ctl, dbPattern, db, isDomain, privs);
|
||||
}
|
||||
|
||||
private static PatternMatcher createDbPatternMatcher(String db) throws AnalysisException {
|
||||
@ -92,17 +100,10 @@ public class DbPrivEntry extends PrivEntry {
|
||||
}
|
||||
|
||||
DbPrivEntry otherEntry = (DbPrivEntry) other;
|
||||
int res = origHost.compareTo(otherEntry.origHost);
|
||||
if (res != 0) {
|
||||
return -res;
|
||||
}
|
||||
|
||||
res = origDb.compareTo(otherEntry.origDb);
|
||||
if (res != 0) {
|
||||
return -res;
|
||||
}
|
||||
|
||||
return -origUser.compareTo(otherEntry.origUser);
|
||||
return compareAssist(origUser, otherEntry.origUser,
|
||||
origHost, otherEntry.origHost,
|
||||
origCtl, otherEntry.origCtl,
|
||||
origDb, otherEntry.origDb);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -112,20 +113,15 @@ public class DbPrivEntry extends PrivEntry {
|
||||
}
|
||||
|
||||
DbPrivEntry otherEntry = (DbPrivEntry) other;
|
||||
if (origHost.equals(otherEntry.origHost) && origUser.equals(otherEntry.origUser)
|
||||
&& origDb.equals(otherEntry.origDb) && isDomain == otherEntry.isDomain) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return origUser.equals(otherEntry.origUser) && origHost.equals(otherEntry.origHost)
|
||||
&& origCtl.equals(otherEntry.origCtl) && origDb.equals(otherEntry.origDb)
|
||||
&& isDomain == otherEntry.isDomain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("db priv. host: ").append(origHost).append(", db: ").append(origDb);
|
||||
sb.append(", user: ").append(origUser);
|
||||
sb.append(", priv: ").append(privSet).append(", set by resolver: ").append(isSetByDomainResolver);
|
||||
return sb.toString();
|
||||
return String.format("database privilege. user: %s, host: %s, ctl: %s, db: %s, priv: %s, set by resolver: %b",
|
||||
origUser, origHost, origCtl, origDb, privSet.toString(), isSetByDomainResolver);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -34,10 +34,10 @@ public class DbPrivTable extends PrivTable {
|
||||
private static final Logger LOG = LogManager.getLogger(DbPrivTable.class);
|
||||
|
||||
/*
|
||||
* Return first priv which match the user@host on db.* The returned priv will be
|
||||
* Return first priv which match the user@host on ctl.db.* The returned priv will be
|
||||
* saved in 'savedPrivs'.
|
||||
*/
|
||||
public void getPrivs(UserIdentity currentUser, String db, PrivBitSet savedPrivs) {
|
||||
public void getPrivs(UserIdentity currentUser, String ctl, String db, PrivBitSet savedPrivs) {
|
||||
DbPrivEntry matchedEntry = null;
|
||||
for (PrivEntry entry : entries) {
|
||||
DbPrivEntry dbPrivEntry = (DbPrivEntry) entry;
|
||||
@ -46,6 +46,11 @@ public class DbPrivTable extends PrivTable {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check catalog
|
||||
if (!dbPrivEntry.isAnyCtl() && !dbPrivEntry.getCtlPattern().match(ctl)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check db
|
||||
if (!dbPrivEntry.isAnyDb() && !dbPrivEntry.getDbPattern().match(db)) {
|
||||
continue;
|
||||
@ -61,28 +66,6 @@ public class DbPrivTable extends PrivTable {
|
||||
savedPrivs.or(matchedEntry.getPrivSet());
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if user@host has specified privilege on any database
|
||||
*/
|
||||
public boolean hasPriv(String host, String user, PrivPredicate wanted) {
|
||||
for (PrivEntry entry : entries) {
|
||||
DbPrivEntry dbPrivEntry = (DbPrivEntry) entry;
|
||||
// check host
|
||||
if (!dbPrivEntry.isAnyHost() && !dbPrivEntry.getHostPattern().match(host)) {
|
||||
continue;
|
||||
}
|
||||
// check user
|
||||
if (!dbPrivEntry.isAnyUser() && !dbPrivEntry.getUserPattern().match(user)) {
|
||||
continue;
|
||||
}
|
||||
// check priv
|
||||
if (dbPrivEntry.privSet.satisfy(wanted)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean hasClusterPriv(ConnectContext ctx, String clusterName) {
|
||||
for (PrivEntry entry : entries) {
|
||||
DbPrivEntry dbPrivEntry = (DbPrivEntry) entry;
|
||||
|
||||
@ -27,6 +27,7 @@ import org.apache.doris.analysis.RevokeStmt;
|
||||
import org.apache.doris.analysis.SetLdapPassVar;
|
||||
import org.apache.doris.analysis.SetPassVar;
|
||||
import org.apache.doris.analysis.SetUserPropertyStmt;
|
||||
import org.apache.doris.analysis.TableName;
|
||||
import org.apache.doris.analysis.TablePattern;
|
||||
import org.apache.doris.analysis.UserIdentity;
|
||||
import org.apache.doris.catalog.AuthorizationInfo;
|
||||
@ -42,6 +43,7 @@ import org.apache.doris.common.LdapConfig;
|
||||
import org.apache.doris.common.Pair;
|
||||
import org.apache.doris.common.UserException;
|
||||
import org.apache.doris.common.io.Writable;
|
||||
import org.apache.doris.datasource.InternalDataSource;
|
||||
import org.apache.doris.ldap.LdapPrivsChecker;
|
||||
import org.apache.doris.load.DppConfig;
|
||||
import org.apache.doris.persist.LdapInfo;
|
||||
@ -53,6 +55,7 @@ import org.apache.doris.thrift.TPrivilegeStatus;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@ -65,6 +68,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PaloAuth implements Writable {
|
||||
private static final Logger LOG = LogManager.getLogger(PaloAuth.class);
|
||||
@ -75,8 +79,10 @@ public class PaloAuth implements Writable {
|
||||
public static final String ADMIN_USER = "admin";
|
||||
// unknown user does not have any privilege, this is just to be compatible with old version.
|
||||
public static final String UNKNOWN_USER = "unknown";
|
||||
private static final String DEFAULT_CATALOG = InternalDataSource.INTERNAL_DS_NAME;
|
||||
|
||||
private UserPrivTable userPrivTable = new UserPrivTable();
|
||||
private CatalogPrivTable catalogPrivTable = new CatalogPrivTable();
|
||||
private DbPrivTable dbPrivTable = new DbPrivTable();
|
||||
private TablePrivTable tablePrivTable = new TablePrivTable();
|
||||
private ResourcePrivTable resourcePrivTable = new ResourcePrivTable();
|
||||
@ -105,7 +111,7 @@ public class PaloAuth implements Writable {
|
||||
}
|
||||
|
||||
public enum PrivLevel {
|
||||
GLOBAL, DATABASE, TABLE, RESOURCE
|
||||
GLOBAL, CATALOG, DATABASE, TABLE, RESOURCE
|
||||
}
|
||||
|
||||
public PaloAuth() {
|
||||
@ -165,12 +171,39 @@ public class PaloAuth implements Writable {
|
||||
false /* not delete entry if priv is empty, because global priv entry has password */);
|
||||
}
|
||||
|
||||
private void grantDbPrivs(UserIdentity userIdentity, String db, boolean errOnExist, boolean errOnNonExist,
|
||||
PrivBitSet privs) throws DdlException {
|
||||
private void grantCatalogPrivs(UserIdentity userIdentity, String ctl,
|
||||
boolean errOnExist, boolean errOnNonExist, PrivBitSet privs) throws DdlException {
|
||||
CatalogPrivEntry entry;
|
||||
try {
|
||||
entry = CatalogPrivEntry.create(userIdentity.getQualifiedUser(), userIdentity.getHost(),
|
||||
ctl, userIdentity.isDomain(), privs);
|
||||
entry.setSetByDomainResolver(false);
|
||||
} catch (AnalysisException e) {
|
||||
throw new DdlException(e.getMessage());
|
||||
}
|
||||
catalogPrivTable.addEntry(entry, errOnExist, errOnNonExist);
|
||||
}
|
||||
|
||||
private void revokeCatalogPrivs(UserIdentity userIdentity, String ctl,
|
||||
PrivBitSet privs, boolean errOnNonExist) throws DdlException {
|
||||
CatalogPrivEntry entry;
|
||||
try {
|
||||
entry = CatalogPrivEntry.create(userIdentity.getQualifiedUser(), userIdentity.getHost(),
|
||||
ctl, userIdentity.isDomain(), privs);
|
||||
entry.setSetByDomainResolver(false);
|
||||
} catch (AnalysisException e) {
|
||||
throw new DdlException(e.getMessage());
|
||||
}
|
||||
|
||||
catalogPrivTable.revoke(entry, errOnNonExist, true /* delete entry when empty */);
|
||||
}
|
||||
|
||||
private void grantDbPrivs(UserIdentity userIdentity, String ctl, String db,
|
||||
boolean errOnExist, boolean errOnNonExist, PrivBitSet privs) throws DdlException {
|
||||
DbPrivEntry entry;
|
||||
try {
|
||||
entry = DbPrivEntry.create(userIdentity.getHost(), db, userIdentity.getQualifiedUser(),
|
||||
userIdentity.isDomain(), privs);
|
||||
entry = DbPrivEntry.create(userIdentity.getQualifiedUser(), userIdentity.getHost(),
|
||||
ctl, db, userIdentity.isDomain(), privs);
|
||||
entry.setSetByDomainResolver(false);
|
||||
} catch (AnalysisException e) {
|
||||
throw new DdlException(e.getMessage());
|
||||
@ -178,12 +211,12 @@ public class PaloAuth implements Writable {
|
||||
dbPrivTable.addEntry(entry, errOnExist, errOnNonExist);
|
||||
}
|
||||
|
||||
private void revokeDbPrivs(UserIdentity userIdentity, String db, PrivBitSet privs, boolean errOnNonExist)
|
||||
throws DdlException {
|
||||
private void revokeDbPrivs(UserIdentity userIdentity, String ctl, String db,
|
||||
PrivBitSet privs, boolean errOnNonExist) throws DdlException {
|
||||
DbPrivEntry entry;
|
||||
try {
|
||||
entry = DbPrivEntry.create(userIdentity.getHost(), db, userIdentity.getQualifiedUser(),
|
||||
userIdentity.isDomain(), privs);
|
||||
entry = DbPrivEntry.create(userIdentity.getQualifiedUser(), userIdentity.getHost(),
|
||||
ctl, db, userIdentity.isDomain(), privs);
|
||||
entry.setSetByDomainResolver(false);
|
||||
} catch (AnalysisException e) {
|
||||
throw new DdlException(e.getMessage());
|
||||
@ -192,12 +225,12 @@ public class PaloAuth implements Writable {
|
||||
dbPrivTable.revoke(entry, errOnNonExist, true /* delete entry when empty */);
|
||||
}
|
||||
|
||||
private void grantTblPrivs(UserIdentity userIdentity, String db, String tbl, boolean errOnExist,
|
||||
boolean errOnNonExist, PrivBitSet privs) throws DdlException {
|
||||
private void grantTblPrivs(UserIdentity userIdentity, String ctl, String db, String tbl,
|
||||
boolean errOnExist, boolean errOnNonExist, PrivBitSet privs) throws DdlException {
|
||||
TablePrivEntry entry;
|
||||
try {
|
||||
entry = TablePrivEntry.create(userIdentity.getHost(), db, userIdentity.getQualifiedUser(), tbl,
|
||||
userIdentity.isDomain(), privs);
|
||||
entry = TablePrivEntry.create(userIdentity.getQualifiedUser(), userIdentity.getHost(),
|
||||
ctl, db, tbl, userIdentity.isDomain(), privs);
|
||||
entry.setSetByDomainResolver(false);
|
||||
} catch (AnalysisException e) {
|
||||
throw new DdlException(e.getMessage());
|
||||
@ -205,12 +238,12 @@ public class PaloAuth implements Writable {
|
||||
tablePrivTable.addEntry(entry, errOnExist, errOnNonExist);
|
||||
}
|
||||
|
||||
private void revokeTblPrivs(UserIdentity userIdentity, String db, String tbl, PrivBitSet privs,
|
||||
boolean errOnNonExist) throws DdlException {
|
||||
private void revokeTblPrivs(UserIdentity userIdentity, String ctl, String db, String tbl,
|
||||
PrivBitSet privs, boolean errOnNonExist) throws DdlException {
|
||||
TablePrivEntry entry;
|
||||
try {
|
||||
entry = TablePrivEntry.create(userIdentity.getHost(), db, userIdentity.getQualifiedUser(), tbl,
|
||||
userIdentity.isDomain(), privs);
|
||||
entry = TablePrivEntry.create(userIdentity.getQualifiedUser(), userIdentity.getHost(),
|
||||
ctl, db, tbl, userIdentity.isDomain(), privs);
|
||||
entry.setSetByDomainResolver(false);
|
||||
} catch (AnalysisException e) {
|
||||
throw new DdlException(e.getMessage());
|
||||
@ -324,11 +357,15 @@ public class PaloAuth implements Writable {
|
||||
return checkDbPriv(ctx.getCurrentUserIdentity(), qualifiedDb, wanted);
|
||||
}
|
||||
|
||||
public boolean checkDbPriv(UserIdentity currentUser, String db, PrivPredicate wanted) {
|
||||
return checkDbPriv(currentUser, DEFAULT_CATALOG, db, wanted);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if 'user'@'host' on 'db' has 'wanted' priv.
|
||||
* If the given db is null, which means it will no check if database name is matched.
|
||||
*/
|
||||
public boolean checkDbPriv(UserIdentity currentUser, String db, PrivPredicate wanted) {
|
||||
public boolean checkDbPriv(UserIdentity currentUser, String ctl, String db, PrivPredicate wanted) {
|
||||
if (!Config.enable_auth_check) {
|
||||
return true;
|
||||
}
|
||||
@ -340,12 +377,13 @@ public class PaloAuth implements Writable {
|
||||
|
||||
PrivBitSet savedPrivs = PrivBitSet.of();
|
||||
if (checkGlobalInternal(currentUser, wanted, savedPrivs)
|
||||
|| checkDbInternal(currentUser, db, wanted, savedPrivs)) {
|
||||
|| checkCatalogInternal(currentUser, ctl, wanted, savedPrivs)
|
||||
|| checkDbInternal(currentUser, ctl, db, wanted, savedPrivs)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// if user has any privs of table in this db, and the wanted priv is SHOW, return true
|
||||
if (db != null && wanted == PrivPredicate.SHOW && checkTblWithDb(currentUser, db)) {
|
||||
if (ctl != null && db != null && wanted == PrivPredicate.SHOW && checkTblWithDb(currentUser, ctl, db)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -358,21 +396,31 @@ public class PaloAuth implements Writable {
|
||||
* So we have to check if user has any privs of tables in this database.
|
||||
* if so, the database should be visible to this user.
|
||||
*/
|
||||
private boolean checkTblWithDb(UserIdentity currentUser, String db) {
|
||||
private boolean checkTblWithDb(UserIdentity currentUser, String ctl, String db) {
|
||||
readLock();
|
||||
try {
|
||||
return (isLdapAuthEnabled() && LdapPrivsChecker.hasPrivsOfDb(currentUser, db))
|
||||
|| tablePrivTable.hasPrivsOfDb(currentUser, db);
|
||||
|| tablePrivTable.hasPrivsOfDb(currentUser, ctl, db);
|
||||
} finally {
|
||||
readUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean checkTblPriv(ConnectContext ctx, String qualifiedDb, String tbl, PrivPredicate wanted) {
|
||||
return checkTblPriv(ctx.getCurrentUserIdentity(), qualifiedDb, tbl, wanted);
|
||||
public boolean checkTblPriv(ConnectContext ctx, String qualifiedCtl,
|
||||
String qualifiedDb, String tbl, PrivPredicate wanted) {
|
||||
return checkTblPriv(ctx.getCurrentUserIdentity(), qualifiedCtl, qualifiedDb, tbl, wanted);
|
||||
}
|
||||
|
||||
public boolean checkTblPriv(UserIdentity currentUser, String db, String tbl, PrivPredicate wanted) {
|
||||
public boolean checkTblPriv(ConnectContext ctx, String qualifiedDb, String tbl, PrivPredicate wanted) {
|
||||
return checkTblPriv(ctx, DEFAULT_CATALOG, qualifiedDb, tbl, wanted);
|
||||
}
|
||||
|
||||
public boolean checkTblPriv(ConnectContext ctx, TableName tableName, PrivPredicate wanted) {
|
||||
Preconditions.checkState(tableName.isFullyQualified());
|
||||
return checkTblPriv(ctx, tableName.getCtl(), tableName.getDb(), wanted);
|
||||
}
|
||||
|
||||
public boolean checkTblPriv(UserIdentity currentUser, String ctl, String db, String tbl, PrivPredicate wanted) {
|
||||
if (!Config.enable_auth_check) {
|
||||
return true;
|
||||
}
|
||||
@ -383,8 +431,9 @@ public class PaloAuth implements Writable {
|
||||
|
||||
PrivBitSet savedPrivs = PrivBitSet.of();
|
||||
if (checkGlobalInternal(currentUser, wanted, savedPrivs)
|
||||
|| checkDbInternal(currentUser, db, wanted, savedPrivs)
|
||||
|| checkTblInternal(currentUser, db, tbl, wanted, savedPrivs)) {
|
||||
|| checkCatalogInternal(currentUser, ctl, wanted, savedPrivs)
|
||||
|| checkDbInternal(currentUser, ctl, db, wanted, savedPrivs)
|
||||
|| checkTblInternal(currentUser, ctl, db, tbl, wanted, savedPrivs)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -392,6 +441,10 @@ public class PaloAuth implements Writable {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean checkTblPriv(UserIdentity currentUser, String db, String tbl, PrivPredicate wanted) {
|
||||
return checkTblPriv(currentUser, DEFAULT_CATALOG, db, tbl, wanted);
|
||||
}
|
||||
|
||||
public boolean checkResourcePriv(ConnectContext ctx, String resourceName, PrivPredicate wanted) {
|
||||
return checkResourcePriv(ctx.getCurrentUserIdentity(), resourceName, wanted);
|
||||
}
|
||||
@ -485,15 +538,12 @@ public class PaloAuth implements Writable {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkDbInternal(UserIdentity currentUser, String db, PrivPredicate wanted,
|
||||
PrivBitSet savedPrivs) {
|
||||
if (isLdapAuthEnabled() && LdapPrivsChecker.hasDbPrivFromLdap(currentUser, db, wanted)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean checkCatalogInternal(UserIdentity currentUser, String ctl,
|
||||
PrivPredicate wanted, PrivBitSet savedPrivs) {
|
||||
// TODO(gaoxin): check privileges by ldap.
|
||||
readLock();
|
||||
try {
|
||||
dbPrivTable.getPrivs(currentUser, db, savedPrivs);
|
||||
catalogPrivTable.getPrivs(currentUser, ctl, savedPrivs);
|
||||
if (PaloPrivilege.satisfy(savedPrivs, wanted)) {
|
||||
return true;
|
||||
}
|
||||
@ -503,7 +553,25 @@ public class PaloAuth implements Writable {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean checkTblInternal(UserIdentity currentUser, String db, String tbl,
|
||||
private boolean checkDbInternal(UserIdentity currentUser, String ctl, String db, PrivPredicate wanted,
|
||||
PrivBitSet savedPrivs) {
|
||||
if (isLdapAuthEnabled() && LdapPrivsChecker.hasDbPrivFromLdap(currentUser, db, wanted)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
readLock();
|
||||
try {
|
||||
dbPrivTable.getPrivs(currentUser, ctl, db, savedPrivs);
|
||||
if (PaloPrivilege.satisfy(savedPrivs, wanted)) {
|
||||
return true;
|
||||
}
|
||||
} finally {
|
||||
readUnlock();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean checkTblInternal(UserIdentity currentUser, String ctl, String db, String tbl,
|
||||
PrivPredicate wanted, PrivBitSet savedPrivs) {
|
||||
if (isLdapAuthEnabled() && LdapPrivsChecker.hasTblPrivFromLdap(currentUser, db, tbl, wanted)) {
|
||||
return true;
|
||||
@ -511,7 +579,7 @@ public class PaloAuth implements Writable {
|
||||
|
||||
readLock();
|
||||
try {
|
||||
tablePrivTable.getPrivs(currentUser, db, tbl, savedPrivs);
|
||||
tablePrivTable.getPrivs(currentUser, ctl, db, tbl, savedPrivs);
|
||||
if (PaloPrivilege.satisfy(savedPrivs, wanted)) {
|
||||
return true;
|
||||
}
|
||||
@ -607,7 +675,7 @@ public class PaloAuth implements Writable {
|
||||
|
||||
if (!userIdent.getQualifiedUser().equals(ROOT_USER) && !userIdent.getQualifiedUser().equals(ADMIN_USER)) {
|
||||
// grant read privs to database information_schema
|
||||
TablePattern tblPattern = new TablePattern(InfoSchemaDb.DATABASE_NAME, "*");
|
||||
TablePattern tblPattern = new TablePattern(DEFAULT_CATALOG, InfoSchemaDb.DATABASE_NAME, "*");
|
||||
try {
|
||||
tblPattern.analyze(ClusterNamespace.getClusterNameFromFullName(userIdent.getQualifiedUser()));
|
||||
} catch (AnalysisException e) {
|
||||
@ -681,6 +749,7 @@ public class PaloAuth implements Writable {
|
||||
|
||||
// we don't check if user exists
|
||||
userPrivTable.dropUser(userIdent);
|
||||
catalogPrivTable.dropUser(userIdent);
|
||||
dbPrivTable.dropUser(userIdent);
|
||||
tablePrivTable.dropUser(userIdent);
|
||||
resourcePrivTable.dropUser(userIdent);
|
||||
@ -815,14 +884,22 @@ public class PaloAuth implements Writable {
|
||||
errOnNonExist,
|
||||
privs);
|
||||
break;
|
||||
case CATALOG:
|
||||
grantCatalogPrivs(userIdent, tblPattern.getQualifiedCtl(),
|
||||
false /* err on exist */,
|
||||
false /* err on non exist */,
|
||||
privs);
|
||||
break;
|
||||
case DATABASE:
|
||||
grantDbPrivs(userIdent, tblPattern.getQualifiedDb(),
|
||||
grantDbPrivs(userIdent, tblPattern.getQualifiedCtl(),
|
||||
tblPattern.getQualifiedDb(),
|
||||
false /* err on exist */,
|
||||
false /* err on non exist */,
|
||||
privs);
|
||||
break;
|
||||
case TABLE:
|
||||
grantTblPrivs(userIdent, tblPattern.getQualifiedDb(),
|
||||
grantTblPrivs(userIdent, tblPattern.getQualifiedCtl(),
|
||||
tblPattern.getQualifiedDb(),
|
||||
tblPattern.getTbl(),
|
||||
false /* err on exist */,
|
||||
false /* err on non exist */,
|
||||
@ -971,12 +1048,16 @@ public class PaloAuth implements Writable {
|
||||
case GLOBAL:
|
||||
revokeGlobalPrivs(userIdent, privs, errOnNonExist);
|
||||
break;
|
||||
case CATALOG:
|
||||
revokeCatalogPrivs(userIdent, tblPattern.getQualifiedCtl(), privs, errOnNonExist);
|
||||
break;
|
||||
case DATABASE:
|
||||
revokeDbPrivs(userIdent, tblPattern.getQualifiedDb(), privs, errOnNonExist);
|
||||
revokeDbPrivs(userIdent, tblPattern.getQualifiedCtl(),
|
||||
tblPattern.getQualifiedDb(), privs, errOnNonExist);
|
||||
break;
|
||||
case TABLE:
|
||||
revokeTblPrivs(userIdent, tblPattern.getQualifiedDb(), tblPattern.getTbl(), privs,
|
||||
errOnNonExist);
|
||||
revokeTblPrivs(userIdent, tblPattern.getQualifiedCtl(), tblPattern.getQualifiedDb(),
|
||||
tblPattern.getTbl(), privs, errOnNonExist);
|
||||
break;
|
||||
default:
|
||||
Preconditions.checkNotNull(null, tblPattern.getPrivLevel());
|
||||
@ -1311,6 +1392,17 @@ public class PaloAuth implements Writable {
|
||||
}
|
||||
}
|
||||
|
||||
// catalog
|
||||
String ctlPrivs = catalogPrivTable.entries.stream()
|
||||
.filter(entry -> entry.match(userIdent, true))
|
||||
.map(entry -> String.format("%s: %s (%b)",
|
||||
((CatalogPrivEntry) entry).getOrigCtl(), entry.privSet, entry.isSetByDomainResolver()))
|
||||
.collect(Collectors.joining("; "));
|
||||
if (Strings.isNullOrEmpty(ctlPrivs)) {
|
||||
ctlPrivs = FeConstants.null_string;
|
||||
}
|
||||
userAuthInfo.add(ctlPrivs);
|
||||
|
||||
// db
|
||||
List<String> dbPrivs = Lists.newArrayList();
|
||||
Set<String> addedDbs = Sets.newHashSet();
|
||||
@ -1326,16 +1418,16 @@ public class PaloAuth implements Writable {
|
||||
PrivBitSet savedPrivs = dEntry.getPrivSet().copy();
|
||||
savedPrivs.or(LdapPrivsChecker.getDbPrivFromLdap(userIdent, dEntry.getOrigDb()));
|
||||
addedDbs.add(dEntry.getOrigDb());
|
||||
dbPrivs.add(dEntry.getOrigDb() + ": " + savedPrivs.toString()
|
||||
+ " (" + entry.isSetByDomainResolver() + ")");
|
||||
dbPrivs.add(String.format("%s.%s: %s (%b)", dEntry.getOrigCtl(), dEntry.getOrigDb(),
|
||||
savedPrivs, dEntry.isSetByDomainResolver()));
|
||||
}
|
||||
// Add privs from ldap groups that have not been added in Doris.
|
||||
if (LdapPrivsChecker.hasLdapPrivs(userIdent)) {
|
||||
Map<TablePattern, PrivBitSet> ldapDbPrivs = LdapPrivsChecker.getLdapAllDbPrivs(userIdent);
|
||||
for (Map.Entry<TablePattern, PrivBitSet> entry : ldapDbPrivs.entrySet()) {
|
||||
if (!addedDbs.contains(entry.getKey().getQualifiedDb())) {
|
||||
dbPrivs.add(entry.getKey().getQualifiedDb() + ": "
|
||||
+ entry.getValue().toString() + " (" + false + ")");
|
||||
dbPrivs.add(String.format("%s.%s: %s (%b)", entry.getKey().getQualifiedCtl(),
|
||||
entry.getKey().getQualifiedDb(), entry.getValue(), false));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1361,17 +1453,15 @@ public class PaloAuth implements Writable {
|
||||
PrivBitSet savedPrivs = tEntry.getPrivSet().copy();
|
||||
savedPrivs.or(LdapPrivsChecker.getTblPrivFromLdap(userIdent, tEntry.getOrigDb(), tEntry.getOrigTbl()));
|
||||
addedtbls.add(tEntry.getOrigDb().concat(".").concat(tEntry.getOrigTbl()));
|
||||
tblPrivs.add(tEntry.getOrigDb() + "." + tEntry.getOrigTbl() + ": "
|
||||
+ savedPrivs.toString()
|
||||
+ " (" + entry.isSetByDomainResolver() + ")");
|
||||
tblPrivs.add(String.format("%s.%s.%s: %s (%b)", tEntry.getOrigCtl(), tEntry.getOrigDb(),
|
||||
tEntry.getOrigTbl(), savedPrivs, tEntry.isSetByDomainResolver()));
|
||||
}
|
||||
// Add privs from ldap groups that have not been added in Doris.
|
||||
if (LdapPrivsChecker.hasLdapPrivs(userIdent)) {
|
||||
Map<TablePattern, PrivBitSet> ldapTblPrivs = LdapPrivsChecker.getLdapAllTblPrivs(userIdent);
|
||||
for (Map.Entry<TablePattern, PrivBitSet> entry : ldapTblPrivs.entrySet()) {
|
||||
if (!addedtbls.contains(entry.getKey().getQualifiedDb().concat(".").concat(entry.getKey().getTbl()))) {
|
||||
tblPrivs.add(entry.getKey().getQualifiedDb().concat(".").concat(entry.getKey().getTbl())
|
||||
.concat(": ").concat(entry.getValue().toString()).concat(" (false)"));
|
||||
tblPrivs.add(String.format("%s: %s (%b)", entry.getKey(), entry.getValue(), false));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1662,6 +1752,7 @@ public class PaloAuth implements Writable {
|
||||
// role manager must be first, because role should be exist before any user
|
||||
roleManager.write(out);
|
||||
userPrivTable.write(out);
|
||||
catalogPrivTable.write(out);
|
||||
dbPrivTable.write(out);
|
||||
tablePrivTable.write(out);
|
||||
resourcePrivTable.write(out);
|
||||
@ -1672,6 +1763,13 @@ public class PaloAuth implements Writable {
|
||||
public void readFields(DataInput in) throws IOException {
|
||||
roleManager = RoleManager.read(in);
|
||||
userPrivTable = (UserPrivTable) PrivTable.read(in);
|
||||
if (Catalog.getCurrentCatalogJournalVersion() >= FeMetaVersion.VERSION_111) {
|
||||
catalogPrivTable = (CatalogPrivTable) PrivTable.read(in);
|
||||
} else {
|
||||
catalogPrivTable = userPrivTable.degradeToInternalCatalogPriv();
|
||||
LOG.info("Load PaloAuth from meta version < {}, degrade UserPrivTable to CatalogPrivTable",
|
||||
FeMetaVersion.VERSION_111);
|
||||
}
|
||||
dbPrivTable = (DbPrivTable) PrivTable.read(in);
|
||||
tablePrivTable = (TablePrivTable) PrivTable.read(in);
|
||||
resourcePrivTable = (ResourcePrivTable) PrivTable.read(in);
|
||||
|
||||
@ -24,6 +24,7 @@ import org.apache.doris.common.PatternMatcher;
|
||||
import org.apache.doris.common.io.Text;
|
||||
import org.apache.doris.common.io.Writable;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.apache.commons.lang.NotImplementedException;
|
||||
|
||||
import java.io.DataInput;
|
||||
@ -253,4 +254,19 @@ public abstract class PrivEntry implements Comparable<PrivEntry>, Writable {
|
||||
public int compareTo(PrivEntry o) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Help derived classes compare in the order of 'user', 'host', 'catalog', 'db', 'ctl'.
|
||||
* Compare strings[i] with strings[i+1] successively, return if the comparison value is not 0 in current loop.
|
||||
*/
|
||||
protected static int compareAssist(String... strings) {
|
||||
Preconditions.checkState(strings.length % 2 == 0);
|
||||
for (int i = 0; i < strings.length; i += 2) {
|
||||
int res = strings[i].compareTo(strings[i + 1]);
|
||||
if (res != 0) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,6 +45,27 @@ public abstract class PrivTable implements Writable {
|
||||
// see PrivEntry for more detail
|
||||
protected boolean isClassNameWrote = false;
|
||||
|
||||
/*
|
||||
* Check if user@host has specified privilege
|
||||
*/
|
||||
public boolean hasPriv(String host, String user, PrivPredicate wanted) {
|
||||
for (PrivEntry entry : entries) {
|
||||
// check host
|
||||
if (!entry.isAnyHost() && !entry.getHostPattern().match(host)) {
|
||||
continue;
|
||||
}
|
||||
// check user
|
||||
if (!entry.isAnyUser() && !entry.getUserPattern().match(user)) {
|
||||
continue;
|
||||
}
|
||||
// check priv
|
||||
if (entry.privSet.satisfy(wanted)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add an entry to priv table.
|
||||
* If entry already exists and errOnExist is false, we try to reset or merge the new priv entry with existing one.
|
||||
|
||||
@ -26,6 +26,7 @@ import org.apache.doris.common.io.Writable;
|
||||
import org.apache.doris.mysql.privilege.PaloAuth.PrivLevel;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
@ -34,6 +35,9 @@ import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class RoleManager implements Writable {
|
||||
private Map<String, PaloRole> roles = Maps.newHashMap();
|
||||
@ -132,60 +136,26 @@ public class RoleManager implements Writable {
|
||||
info.add(role.getRoleName());
|
||||
info.add(Joiner.on(", ").join(role.getUsers()));
|
||||
|
||||
// global
|
||||
boolean hasGlobal = false;
|
||||
for (Map.Entry<TablePattern, PrivBitSet> entry : role.getTblPatternToPrivs().entrySet()) {
|
||||
if (entry.getKey().getPrivLevel() == PrivLevel.GLOBAL) {
|
||||
hasGlobal = true;
|
||||
info.add(entry.getValue().toString());
|
||||
// global priv should only has one
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasGlobal) {
|
||||
info.add(FeConstants.null_string);
|
||||
}
|
||||
|
||||
// db
|
||||
List<String> tmp = Lists.newArrayList();
|
||||
for (Map.Entry<TablePattern, PrivBitSet> entry : role.getTblPatternToPrivs().entrySet()) {
|
||||
if (entry.getKey().getPrivLevel() == PrivLevel.DATABASE) {
|
||||
tmp.add(entry.getKey().toString() + ": " + entry.getValue().toString());
|
||||
}
|
||||
}
|
||||
if (tmp.isEmpty()) {
|
||||
info.add(FeConstants.null_string);
|
||||
} else {
|
||||
info.add(Joiner.on("; ").join(tmp));
|
||||
}
|
||||
|
||||
|
||||
// tbl
|
||||
tmp.clear();
|
||||
for (Map.Entry<TablePattern, PrivBitSet> entry : role.getTblPatternToPrivs().entrySet()) {
|
||||
if (entry.getKey().getPrivLevel() == PrivLevel.TABLE) {
|
||||
tmp.add(entry.getKey().toString() + ": " + entry.getValue().toString());
|
||||
}
|
||||
}
|
||||
if (tmp.isEmpty()) {
|
||||
info.add(FeConstants.null_string);
|
||||
} else {
|
||||
info.add(Joiner.on("; ").join(tmp));
|
||||
}
|
||||
|
||||
// resource
|
||||
tmp.clear();
|
||||
for (Map.Entry<ResourcePattern, PrivBitSet> entry : role.getResourcePatternToPrivs().entrySet()) {
|
||||
if (entry.getKey().getPrivLevel() == PrivLevel.RESOURCE) {
|
||||
tmp.add(entry.getKey().toString() + ": " + entry.getValue().toString());
|
||||
}
|
||||
}
|
||||
if (tmp.isEmpty()) {
|
||||
info.add(FeConstants.null_string);
|
||||
} else {
|
||||
info.add(Joiner.on("; ").join(tmp));
|
||||
}
|
||||
|
||||
Map<PrivLevel, String> infoMap = role.getTblPatternToPrivs().entrySet().stream()
|
||||
.collect(Collectors.groupingBy(entry -> entry.getKey().getPrivLevel())).entrySet().stream()
|
||||
.collect(Collectors.toMap(Entry::getKey, entry -> {
|
||||
if (entry.getKey() == PrivLevel.GLOBAL) {
|
||||
return entry.getValue().stream().findFirst().map(priv -> priv.getValue().toString())
|
||||
.orElse(FeConstants.null_string);
|
||||
} else {
|
||||
return entry.getValue().stream()
|
||||
.map(priv -> priv.getKey() + ": " + priv.getValue())
|
||||
.collect(Collectors.joining("; "));
|
||||
}
|
||||
}));
|
||||
Stream.of(PrivLevel.GLOBAL, PrivLevel.CATALOG, PrivLevel.DATABASE, PrivLevel.TABLE, PrivLevel.RESOURCE)
|
||||
.forEach(level -> {
|
||||
String infoItem = infoMap.get(level);
|
||||
if (Strings.isNullOrEmpty(infoItem)) {
|
||||
infoItem = FeConstants.null_string;
|
||||
}
|
||||
info.add(infoItem);
|
||||
});
|
||||
results.add(info);
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,10 +36,13 @@ public class TablePrivEntry extends DbPrivEntry {
|
||||
protected TablePrivEntry() {
|
||||
}
|
||||
|
||||
private TablePrivEntry(PatternMatcher hostPattern, String origHost, PatternMatcher dbPattern, String origDb,
|
||||
PatternMatcher userPattern, String user, PatternMatcher tblPattern, String origTbl,
|
||||
boolean isDomain, PrivBitSet privSet) {
|
||||
super(hostPattern, origHost, dbPattern, origDb, userPattern, user, isDomain, privSet);
|
||||
private TablePrivEntry(PatternMatcher userPattern, String user,
|
||||
PatternMatcher hostPattern, String origHost,
|
||||
PatternMatcher ctlPattern, String origCtl,
|
||||
PatternMatcher dbPattern, String origDb,
|
||||
PatternMatcher tblPattern, String origTbl,
|
||||
boolean isDomain, PrivBitSet privSet) {
|
||||
super(userPattern, user, hostPattern, origHost, ctlPattern, origCtl, dbPattern, origDb, isDomain, privSet);
|
||||
this.tblPattern = tblPattern;
|
||||
this.origTbl = origTbl;
|
||||
if (origTbl.equals(ANY_TBL)) {
|
||||
@ -47,12 +50,15 @@ public class TablePrivEntry extends DbPrivEntry {
|
||||
}
|
||||
}
|
||||
|
||||
public static TablePrivEntry create(String host, String db, String user, String tbl, boolean isDomain,
|
||||
PrivBitSet privs) throws AnalysisException {
|
||||
public static TablePrivEntry create(String user, String host,
|
||||
String ctl, String db, String tbl,
|
||||
boolean isDomain, PrivBitSet privs) throws AnalysisException {
|
||||
PatternMatcher hostPattern = PatternMatcher.createMysqlPattern(host, CaseSensibility.HOST.getCaseSensibility());
|
||||
PatternMatcher dbPattern = PatternMatcher.createFlatPattern(
|
||||
db, CaseSensibility.DATABASE.getCaseSensibility(), db.equals(ANY_DB));
|
||||
PatternMatcher userPattern = PatternMatcher.createFlatPattern(user, CaseSensibility.USER.getCaseSensibility());
|
||||
PatternMatcher ctlPattern = PatternMatcher.createFlatPattern(
|
||||
ctl, CaseSensibility.CATALOG.getCaseSensibility(), ctl.equals(ANY_CTL));
|
||||
|
||||
PatternMatcher tblPattern = PatternMatcher.createFlatPattern(
|
||||
tbl, CaseSensibility.TABLE.getCaseSensibility(), tbl.equals(ANY_TBL));
|
||||
@ -61,8 +67,8 @@ public class TablePrivEntry extends DbPrivEntry {
|
||||
throw new AnalysisException("Table privilege can not contains global or resource privileges: " + privs);
|
||||
}
|
||||
|
||||
return new TablePrivEntry(hostPattern, host, dbPattern, db,
|
||||
userPattern, user, tblPattern, tbl, isDomain, privs);
|
||||
return new TablePrivEntry(userPattern, user, hostPattern, host,
|
||||
ctlPattern, ctl, dbPattern, db, tblPattern, tbl, isDomain, privs);
|
||||
}
|
||||
|
||||
public PatternMatcher getTblPattern() {
|
||||
@ -84,22 +90,11 @@ public class TablePrivEntry extends DbPrivEntry {
|
||||
}
|
||||
|
||||
TablePrivEntry otherEntry = (TablePrivEntry) other;
|
||||
int res = origHost.compareTo(otherEntry.origHost);
|
||||
if (res != 0) {
|
||||
return -res;
|
||||
}
|
||||
|
||||
res = origDb.compareTo(otherEntry.origDb);
|
||||
if (res != 0) {
|
||||
return -res;
|
||||
}
|
||||
|
||||
res = origUser.compareTo(otherEntry.origUser);
|
||||
if (res != 0) {
|
||||
return -res;
|
||||
}
|
||||
|
||||
return -origTbl.compareTo(otherEntry.origTbl);
|
||||
return compareAssist(origUser, otherEntry.origUser,
|
||||
origHost, otherEntry.origHost,
|
||||
origCtl, otherEntry.origCtl,
|
||||
origDb, otherEntry.origDb,
|
||||
origTbl, otherEntry.origTbl);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -109,21 +104,16 @@ public class TablePrivEntry extends DbPrivEntry {
|
||||
}
|
||||
|
||||
TablePrivEntry otherEntry = (TablePrivEntry) other;
|
||||
if (origHost.equals(otherEntry.origHost) && origUser.equals(otherEntry.origUser)
|
||||
&& origDb.equals(otherEntry.origDb) && origTbl.equals(otherEntry.origTbl)
|
||||
&& isDomain == otherEntry.isDomain) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return origUser.equals(otherEntry.origUser) && origHost.equals(otherEntry.origHost)
|
||||
&& origCtl.equals(otherEntry.origCtl) && origDb.equals(otherEntry.origDb)
|
||||
&& origTbl.equals(otherEntry.origTbl) && isDomain == otherEntry.isDomain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("db priv. host: ").append(origHost).append(", db: ").append(origDb);
|
||||
sb.append(", user: ").append(origUser).append(", tbl: ").append(origTbl);
|
||||
sb.append(", priv: ").append(privSet).append(", set by resolver: ").append(isSetByDomainResolver);
|
||||
return sb.toString();
|
||||
return String.format("table privilege. user: %s, host: %s, "
|
||||
+ "ctl: %s, db: %s, tbl: %s, priv: %s, set by resolver: %b",
|
||||
origUser, origHost, origCtl, origDb, origTbl, privSet.toString(), isSetByDomainResolver);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -32,10 +32,10 @@ import java.io.IOException;
|
||||
public class TablePrivTable extends PrivTable {
|
||||
|
||||
/*
|
||||
* Return first priv which match the user@host on db.tbl The returned priv will
|
||||
* Return first priv which match the user@host on ctl.db.tbl The returned priv will
|
||||
* be saved in 'savedPrivs'.
|
||||
*/
|
||||
public void getPrivs(UserIdentity currentUser, String db, String tbl, PrivBitSet savedPrivs) {
|
||||
public void getPrivs(UserIdentity currentUser, String ctl, String db, String tbl, PrivBitSet savedPrivs) {
|
||||
TablePrivEntry matchedEntry = null;
|
||||
for (PrivEntry entry : entries) {
|
||||
TablePrivEntry tblPrivEntry = (TablePrivEntry) entry;
|
||||
@ -43,6 +43,11 @@ public class TablePrivTable extends PrivTable {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check catalog
|
||||
if (!tblPrivEntry.isAnyCtl() && !tblPrivEntry.getCtlPattern().match(ctl)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check db
|
||||
Preconditions.checkState(!tblPrivEntry.isAnyDb());
|
||||
if (!tblPrivEntry.getDbPattern().match(db)) {
|
||||
@ -64,29 +69,7 @@ public class TablePrivTable extends PrivTable {
|
||||
savedPrivs.or(matchedEntry.getPrivSet());
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if user@host has specified privilege on any table
|
||||
*/
|
||||
public boolean hasPriv(String host, String user, PrivPredicate wanted) {
|
||||
for (PrivEntry entry : entries) {
|
||||
TablePrivEntry tblPrivEntry = (TablePrivEntry) entry;
|
||||
// check host
|
||||
if (!tblPrivEntry.isAnyHost() && !tblPrivEntry.getHostPattern().match(host)) {
|
||||
continue;
|
||||
}
|
||||
// check user
|
||||
if (!tblPrivEntry.isAnyUser() && !tblPrivEntry.getUserPattern().match(user)) {
|
||||
continue;
|
||||
}
|
||||
// check priv
|
||||
if (tblPrivEntry.privSet.satisfy(wanted)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean hasPrivsOfDb(UserIdentity currentUser, String db) {
|
||||
public boolean hasPrivsOfDb(UserIdentity currentUser, String ctl, String db) {
|
||||
for (PrivEntry entry : entries) {
|
||||
TablePrivEntry tblPrivEntry = (TablePrivEntry) entry;
|
||||
|
||||
@ -94,6 +77,12 @@ public class TablePrivTable extends PrivTable {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check catalog
|
||||
Preconditions.checkState(!tblPrivEntry.isAnyCtl());
|
||||
if (!tblPrivEntry.getCtlPattern().match(ctl)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check db
|
||||
Preconditions.checkState(!tblPrivEntry.isAnyDb());
|
||||
if (!tblPrivEntry.getDbPattern().match(db)) {
|
||||
|
||||
@ -20,6 +20,7 @@ package org.apache.doris.mysql.privilege;
|
||||
import org.apache.doris.analysis.UserIdentity;
|
||||
import org.apache.doris.common.DdlException;
|
||||
import org.apache.doris.common.io.Text;
|
||||
import org.apache.doris.datasource.InternalDataSource;
|
||||
import org.apache.doris.mysql.MysqlPassword;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@ -27,6 +28,7 @@ import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
@ -57,27 +59,6 @@ public class UserPrivTable extends PrivTable {
|
||||
savedPrivs.or(matchedEntry.getPrivSet());
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if user@host has specified privilege
|
||||
*/
|
||||
public boolean hasPriv(String host, String user, PrivPredicate wanted) {
|
||||
for (PrivEntry entry : entries) {
|
||||
GlobalPrivEntry globalPrivEntry = (GlobalPrivEntry) entry;
|
||||
// check host
|
||||
if (!globalPrivEntry.isAnyHost() && !globalPrivEntry.getHostPattern().match(host)) {
|
||||
continue;
|
||||
}
|
||||
// check user
|
||||
if (!globalPrivEntry.isAnyUser() && !globalPrivEntry.getUserPattern().match(user)) {
|
||||
continue;
|
||||
}
|
||||
if (globalPrivEntry.getPrivSet().satisfy(wanted)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// validate the connection by host, user and password.
|
||||
// return true if this connection is valid, and 'savedPrivs' save all global privs got from user table.
|
||||
// if currentUser is not null, save the current user identity
|
||||
@ -196,4 +177,33 @@ public class UserPrivTable extends PrivTable {
|
||||
|
||||
super.write(out);
|
||||
}
|
||||
|
||||
/**
|
||||
* When replay UserPrivTable from journal whose FeMetaVersion < VERSION_111, the global-level privileges should
|
||||
* degrade to internal-catalog-level privileges.
|
||||
*/
|
||||
public CatalogPrivTable degradeToInternalCatalogPriv() throws IOException {
|
||||
CatalogPrivTable catalogPrivTable = new CatalogPrivTable();
|
||||
List<PrivEntry> degradedEntries = new LinkedList<>();
|
||||
for (PrivEntry privEntry : entries) {
|
||||
GlobalPrivEntry globalPrivEntry = (GlobalPrivEntry) privEntry;
|
||||
if (!globalPrivEntry.match(UserIdentity.ROOT, true)
|
||||
&& !globalPrivEntry.match(UserIdentity.ADMIN, true)
|
||||
&& !globalPrivEntry.privSet.isEmpty()) {
|
||||
try {
|
||||
CatalogPrivEntry entry = CatalogPrivEntry.create(globalPrivEntry.origUser, globalPrivEntry.origHost,
|
||||
InternalDataSource.INTERNAL_DS_NAME, globalPrivEntry.isDomain, globalPrivEntry.privSet);
|
||||
entry.setSetByDomainResolver(false);
|
||||
catalogPrivTable.addEntry(entry, false, false);
|
||||
degradedEntries.add(globalPrivEntry);
|
||||
} catch (Exception e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (PrivEntry degraded : degradedEntries) {
|
||||
dropEntry(degraded);
|
||||
}
|
||||
return catalogPrivTable;
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,6 +23,7 @@ import org.apache.doris.catalog.Database;
|
||||
import org.apache.doris.cluster.ClusterNamespace;
|
||||
import org.apache.doris.common.UserException;
|
||||
import org.apache.doris.common.util.DebugUtil;
|
||||
import org.apache.doris.datasource.InternalDataSource;
|
||||
import org.apache.doris.datasource.SessionContext;
|
||||
import org.apache.doris.mysql.MysqlCapability;
|
||||
import org.apache.doris.mysql.MysqlChannel;
|
||||
@ -108,6 +109,7 @@ public class ConnectContext {
|
||||
// Catalog: put catalog here is convenient for unit test,
|
||||
// because catalog is singleton, hard to mock
|
||||
protected Catalog catalog;
|
||||
protected String defaultCatalog = InternalDataSource.INTERNAL_DS_NAME;
|
||||
protected boolean isSend;
|
||||
|
||||
protected AuditEventBuilder auditEventBuilder = new AuditEventBuilder();
|
||||
@ -290,6 +292,7 @@ public class ConnectContext {
|
||||
|
||||
public void setCatalog(Catalog catalog) {
|
||||
this.catalog = catalog;
|
||||
defaultCatalog = catalog.getInternalDataSource().getName();
|
||||
}
|
||||
|
||||
public Catalog getCatalog() {
|
||||
@ -410,6 +413,14 @@ public class ConnectContext {
|
||||
return serverCapability;
|
||||
}
|
||||
|
||||
public String getDefaultCatalog() {
|
||||
return defaultCatalog;
|
||||
}
|
||||
|
||||
public void changeDefaultCatalog(String catalogName) {
|
||||
defaultCatalog = catalogName;
|
||||
}
|
||||
|
||||
public String getDatabase() {
|
||||
return currentDb;
|
||||
}
|
||||
|
||||
@ -33,6 +33,7 @@ import org.apache.doris.catalog.SinglePartitionInfo;
|
||||
import org.apache.doris.common.AnalysisException;
|
||||
import org.apache.doris.common.DdlException;
|
||||
import org.apache.doris.common.jmockit.Deencapsulation;
|
||||
import org.apache.doris.datasource.InternalDataSource;
|
||||
import org.apache.doris.load.Load;
|
||||
import org.apache.doris.mysql.privilege.PaloAuth;
|
||||
import org.apache.doris.mysql.privilege.PrivPredicate;
|
||||
@ -319,6 +320,10 @@ public class AccessTestUtil {
|
||||
Analyzer analyzer = new Analyzer(fetchAdminCatalog(), new ConnectContext(null));
|
||||
new Expectations(analyzer) {
|
||||
{
|
||||
analyzer.getDefaultCatalog();
|
||||
minTimes = 0;
|
||||
result = InternalDataSource.INTERNAL_DS_NAME;
|
||||
|
||||
analyzer.getDefaultDb();
|
||||
minTimes = 0;
|
||||
result = withCluster ? prefix + "testDb" : "testDb";
|
||||
@ -351,6 +356,10 @@ public class AccessTestUtil {
|
||||
Analyzer analyzer = new Analyzer(fetchBlockCatalog(), new ConnectContext(null));
|
||||
new Expectations(analyzer) {
|
||||
{
|
||||
analyzer.getDefaultCatalog();
|
||||
minTimes = 0;
|
||||
result = InternalDataSource.INTERNAL_DS_NAME;
|
||||
|
||||
analyzer.getDefaultDb();
|
||||
minTimes = 0;
|
||||
result = "testCluster:testDb";
|
||||
@ -371,6 +380,10 @@ public class AccessTestUtil {
|
||||
Analyzer analyzer = new Analyzer(fetchBlockCatalog(), new ConnectContext(null));
|
||||
new Expectations(analyzer) {
|
||||
{
|
||||
analyzer.getDefaultCatalog();
|
||||
minTimes = 0;
|
||||
result = InternalDataSource.INTERNAL_DS_NAME;
|
||||
|
||||
analyzer.getDefaultDb();
|
||||
minTimes = 0;
|
||||
result = "";
|
||||
@ -479,6 +492,10 @@ public class AccessTestUtil {
|
||||
Analyzer analyzer = new Analyzer(catalog, new ConnectContext(null));
|
||||
new Expectations(analyzer) {
|
||||
{
|
||||
analyzer.getDefaultCatalog();
|
||||
minTimes = 0;
|
||||
result = InternalDataSource.INTERNAL_DS_NAME;
|
||||
|
||||
analyzer.getDefaultDb();
|
||||
minTimes = 0;
|
||||
result = "testDb";
|
||||
|
||||
@ -19,6 +19,7 @@ package org.apache.doris.analysis;
|
||||
|
||||
import org.apache.doris.common.AnalysisException;
|
||||
import org.apache.doris.common.UserException;
|
||||
import org.apache.doris.datasource.InternalDataSource;
|
||||
import org.apache.doris.mysql.privilege.MockedAuth;
|
||||
import org.apache.doris.mysql.privilege.PaloAuth;
|
||||
import org.apache.doris.qe.ConnectContext;
|
||||
@ -49,6 +50,10 @@ public class DropTableStmtTest {
|
||||
|
||||
new Expectations() {
|
||||
{
|
||||
noDbAnalyzer.getDefaultCatalog();
|
||||
minTimes = 0;
|
||||
result = InternalDataSource.INTERNAL_DS_NAME;
|
||||
|
||||
noDbAnalyzer.getDefaultDb();
|
||||
minTimes = 0;
|
||||
result = "";
|
||||
|
||||
@ -19,6 +19,8 @@ package org.apache.doris.analysis;
|
||||
|
||||
import org.apache.doris.catalog.Type;
|
||||
import org.apache.doris.common.AnalysisException;
|
||||
import org.apache.doris.common.FeMetaVersion;
|
||||
import org.apache.doris.meta.MetaContext;
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
@ -49,6 +51,10 @@ public class VirtualSlotRefTest {
|
||||
@Before
|
||||
public void setUp() throws IOException, AnalysisException {
|
||||
Analyzer analyzerBase = AccessTestUtil.fetchTableAnalyzer();
|
||||
// read objects from file
|
||||
MetaContext metaContext = new MetaContext();
|
||||
metaContext.setMetaVersion(FeMetaVersion.VERSION_CURRENT);
|
||||
metaContext.setThreadLocalInfo();
|
||||
analyzer = new Analyzer(analyzerBase.getCatalog(), analyzerBase.getContext());
|
||||
String[] cols = {"k1", "k2", "k3"};
|
||||
slots = new ArrayList<>();
|
||||
|
||||
@ -22,6 +22,7 @@ import org.apache.doris.analysis.TablePattern;
|
||||
import org.apache.doris.analysis.UserIdentity;
|
||||
import org.apache.doris.common.AnalysisException;
|
||||
import org.apache.doris.common.LdapConfig;
|
||||
import org.apache.doris.datasource.InternalDataSource;
|
||||
import org.apache.doris.mysql.privilege.PaloPrivilege;
|
||||
import org.apache.doris.mysql.privilege.PaloRole;
|
||||
import org.apache.doris.mysql.privilege.PrivBitSet;
|
||||
@ -38,6 +39,7 @@ import java.util.Map;
|
||||
|
||||
public class LdapPrivsCheckerTest {
|
||||
private static final String CLUSTER = "default_cluster";
|
||||
private static final String INTERNAL = InternalDataSource.INTERNAL_DS_NAME;
|
||||
private static final String DB = "palodb";
|
||||
private static final String TABLE_DB = "tabledb";
|
||||
private static final String TABLE1 = "table1";
|
||||
@ -63,13 +65,13 @@ public class LdapPrivsCheckerTest {
|
||||
PaloRole role = new PaloRole("");
|
||||
Map<TablePattern, PrivBitSet> tblPatternToPrivs = role.getTblPatternToPrivs();
|
||||
|
||||
TablePattern global = new TablePattern("*", "*");
|
||||
TablePattern global = new TablePattern("*", "*", "*");
|
||||
tblPatternToPrivs.put(global, PrivBitSet.of(PaloPrivilege.SELECT_PRIV, PaloPrivilege.CREATE_PRIV));
|
||||
TablePattern db = new TablePattern(DB, "*");
|
||||
TablePattern db = new TablePattern(INTERNAL, DB, "*");
|
||||
tblPatternToPrivs.put(db, PrivBitSet.of(PaloPrivilege.SELECT_PRIV, PaloPrivilege.LOAD_PRIV));
|
||||
TablePattern tbl1 = new TablePattern(TABLE_DB, TABLE1);
|
||||
TablePattern tbl1 = new TablePattern(INTERNAL, TABLE_DB, TABLE1);
|
||||
tblPatternToPrivs.put(tbl1, PrivBitSet.of(PaloPrivilege.SELECT_PRIV, PaloPrivilege.ALTER_PRIV));
|
||||
TablePattern tbl2 = new TablePattern(TABLE_DB, TABLE2);
|
||||
TablePattern tbl2 = new TablePattern(INTERNAL, TABLE_DB, TABLE2);
|
||||
tblPatternToPrivs.put(tbl2, PrivBitSet.of(PaloPrivilege.SELECT_PRIV, PaloPrivilege.DROP_PRIV));
|
||||
|
||||
Map<ResourcePattern, PrivBitSet> resourcePatternToPrivs = role.getResourcePatternToPrivs();
|
||||
|
||||
@ -23,6 +23,7 @@ import org.apache.doris.catalog.Database;
|
||||
import org.apache.doris.cluster.ClusterNamespace;
|
||||
import org.apache.doris.common.DdlException;
|
||||
import org.apache.doris.common.LdapConfig;
|
||||
import org.apache.doris.datasource.InternalDataSource;
|
||||
import org.apache.doris.ldap.LdapAuthenticate;
|
||||
import org.apache.doris.ldap.LdapClient;
|
||||
import org.apache.doris.mysql.privilege.PaloAuth;
|
||||
@ -82,6 +83,10 @@ public class MysqlProtoTest {
|
||||
}
|
||||
};
|
||||
|
||||
catalog.getInternalDataSource();
|
||||
minTimes = 0;
|
||||
result = new InternalDataSource();
|
||||
|
||||
catalog.getDbNullable(anyString);
|
||||
minTimes = 0;
|
||||
result = new Database();
|
||||
|
||||
@ -35,6 +35,7 @@ import org.apache.doris.common.AnalysisException;
|
||||
import org.apache.doris.common.Config;
|
||||
import org.apache.doris.common.DdlException;
|
||||
import org.apache.doris.common.UserException;
|
||||
import org.apache.doris.datasource.InternalDataSource;
|
||||
import org.apache.doris.persist.EditLog;
|
||||
import org.apache.doris.persist.PrivInfo;
|
||||
import org.apache.doris.qe.ConnectContext;
|
||||
@ -102,6 +103,10 @@ public class AuthTest {
|
||||
minTimes = 0;
|
||||
result = SystemInfoService.DEFAULT_CLUSTER;
|
||||
|
||||
analyzer.getDefaultCatalog();
|
||||
minTimes = 0;
|
||||
result = InternalDataSource.INTERNAL_DS_NAME;
|
||||
|
||||
Catalog.getCurrentCatalog();
|
||||
minTimes = 0;
|
||||
result = catalog;
|
||||
@ -1242,7 +1247,7 @@ public class AuthTest {
|
||||
}
|
||||
};
|
||||
Assert.assertFalse(auth.checkGlobalPriv(ctx, PrivPredicate.OPERATOR));
|
||||
grantStmt = new GrantStmt(opUser, null, new TablePattern("*", "*"), privileges);
|
||||
grantStmt = new GrantStmt(opUser, null, new TablePattern("*", "*", "*"), privileges);
|
||||
// first, use op_user itself to grant node_priv, which is not allowed
|
||||
try {
|
||||
new Expectations() {
|
||||
|
||||
@ -26,7 +26,7 @@ public class PrivEntryTest {
|
||||
@Test
|
||||
public void testNameWithUnderscores() throws Exception {
|
||||
TablePrivEntry tablePrivEntry = TablePrivEntry.create(
|
||||
"127.%", "db_db1", "user1", "tbl_tbl1", false,
|
||||
"user1", "127.%", "__internal", "db_db1", "tbl_tbl1", false,
|
||||
PrivBitSet.of(PaloPrivilege.SELECT_PRIV, PaloPrivilege.DROP_PRIV));
|
||||
// pattern match
|
||||
Assert.assertFalse(tablePrivEntry.getDbPattern().match("db-db1"));
|
||||
@ -38,11 +38,11 @@ public class PrivEntryTest {
|
||||
userIdentity.setIsAnalyzed();
|
||||
|
||||
PrivBitSet privs1 = PrivBitSet.of();
|
||||
tablePrivTable.getPrivs(userIdentity, "db#db1", "tbl#tbl1", privs1);
|
||||
tablePrivTable.getPrivs(userIdentity, "##internal", "db#db1", "tbl#tbl1", privs1);
|
||||
Assert.assertFalse(PaloPrivilege.satisfy(privs1, PrivPredicate.DROP));
|
||||
|
||||
PrivBitSet privs2 = PrivBitSet.of();
|
||||
tablePrivTable.getPrivs(userIdentity, "db_db1", "tbl_tbl1", privs2);
|
||||
tablePrivTable.getPrivs(userIdentity, "__internal", "db_db1", "tbl_tbl1", privs2);
|
||||
Assert.assertTrue(PaloPrivilege.satisfy(privs2, PrivPredicate.DROP));
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@ public class PolicyTest extends TestWithFeService {
|
||||
CreateUserStmt createUserStmt = new CreateUserStmt(new UserDesc(user));
|
||||
Catalog.getCurrentCatalog().getAuth().createUser(createUserStmt);
|
||||
List<AccessPrivilege> privileges = Lists.newArrayList(AccessPrivilege.ADMIN_PRIV);
|
||||
TablePattern tablePattern = new TablePattern("*", "*");
|
||||
TablePattern tablePattern = new TablePattern("*", "*", "*");
|
||||
tablePattern.analyze(SystemInfoService.DEFAULT_CLUSTER);
|
||||
GrantStmt grantStmt = new GrantStmt(user, null, tablePattern, privileges);
|
||||
Catalog.getCurrentCatalog().getAuth().grant(grantStmt);
|
||||
|
||||
Reference in New Issue
Block a user