This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new a2e00727d6 [feature](auth)support Col auth (#22629)
a2e00727d6 is described below
commit a2e00727d68b329e5c6a5fda59d3e915f9649d05
Author: zhangdong <[email protected]>
AuthorDate: Tue Aug 15 15:32:51 2023 +0800
[feature](auth)support Col auth (#22629)
support GRANT privilege[(col1,col2...)] [, privilege] ON db.tbl TO
user_identity [ROLE 'role'];
---
fe/fe-core/src/main/cup/sql_parser.cup | 14 +-
.../java/org/apache/doris/analysis/GrantStmt.java | 100 ++++++++---
.../java/org/apache/doris/analysis/RevokeStmt.java | 55 ++++---
.../org/apache/doris/catalog/AccessPrivilege.java | 32 ++--
.../doris/catalog/AccessPrivilegeWithCols.java | 87 ++++++++++
.../org/apache/doris/common/proc/AuthProcDir.java | 3 +-
.../org/apache/doris/mysql/privilege/Auth.java | 71 ++++++--
.../doris/mysql/privilege/ColPrivilegeKey.java | 98 +++++++++++
.../apache/doris/mysql/privilege/PrivBitSet.java | 3 +-
.../doris/mysql/privilege/PrivPredicate.java | 16 +-
.../org/apache/doris/mysql/privilege/Role.java | 134 ++++++++++++++-
.../apache/doris/mysql/privilege/RoleManager.java | 6 +-
.../java/org/apache/doris/persist/PrivInfo.java | 12 +-
.../org/apache/doris/analysis/GrantStmtTest.java | 9 +-
.../doris/cooldown/CooldownConfHandlerTest.java | 3 +-
.../org/apache/doris/mysql/privilege/AuthTest.java | 182 +++++++++++++++++----
.../nereids/rules/analysis/CheckRowPolicyTest.java | 6 +-
.../org/apache/doris/persist/PrivInfoTest.java | 2 +-
.../java/org/apache/doris/policy/PolicyTest.java | 7 +-
19 files changed, 713 insertions(+), 127 deletions(-)
diff --git a/fe/fe-core/src/main/cup/sql_parser.cup
b/fe/fe-core/src/main/cup/sql_parser.cup
index d589ff3937..aff48db22a 100644
--- a/fe/fe-core/src/main/cup/sql_parser.cup
+++ b/fe/fe-core/src/main/cup/sql_parser.cup
@@ -35,6 +35,7 @@ import org.apache.doris.analysis.SetOperationStmt.Operation;
import org.apache.doris.analysis.SetOperationStmt.SetOperand;
import org.apache.doris.analysis.LoadType;
import org.apache.doris.catalog.AccessPrivilege;
+import org.apache.doris.catalog.AccessPrivilegeWithCols;
import org.apache.doris.catalog.AggregateType;
import org.apache.doris.catalog.KeysType;
import org.apache.doris.catalog.PrimitiveType;
@@ -851,10 +852,10 @@ nonterminal List<AllPartitionDesc>
all_partition_desc_list;
nonterminal PartitionKeyDesc fixed_multi_partition_key_desc;
nonterminal MultiPartitionDesc multi_partition_desc;
-nonterminal List<AccessPrivilege> privilege_list;
+nonterminal List<AccessPrivilegeWithCols> privilege_list;
nonterminal List<String> string_list;
nonterminal List<Long> integer_list, cancel_rollup_job_id_list;
-nonterminal AccessPrivilege privilege_type;
+nonterminal AccessPrivilegeWithCols privilege_type;
nonterminal DataDescription data_desc, mysql_data_desc;
nonterminal List<DataDescription> data_desc_list;
@@ -7108,16 +7109,17 @@ column_slice ::=
;
privilege_type ::=
- ident:name
+ ident:name opt_col_list:cols
{:
- RESULT = AccessPrivilege.fromName(name);
- if (RESULT == null) {
+ AccessPrivilege accessPrivilege = AccessPrivilege.fromName(name);
+ if (accessPrivilege == null) {
throw new AnalysisException("Unknown privilege type " + name);
}
+ RESULT = new AccessPrivilegeWithCols(accessPrivilege, cols);
:}
| KW_ALL:id
{:
- RESULT = AccessPrivilege.ALL;
+ RESULT = new AccessPrivilegeWithCols(AccessPrivilege.ALL);
:}
;
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/GrantStmt.java
b/fe/fe-core/src/main/java/org/apache/doris/analysis/GrantStmt.java
index 7608218397..aa0c21f0f0 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/GrantStmt.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/GrantStmt.java
@@ -17,7 +17,7 @@
package org.apache.doris.analysis;
-import org.apache.doris.catalog.AccessPrivilege;
+import org.apache.doris.catalog.AccessPrivilegeWithCols;
import org.apache.doris.catalog.Env;
import org.apache.doris.cluster.ClusterNamespace;
import org.apache.doris.common.AnalysisException;
@@ -26,19 +26,26 @@ import org.apache.doris.common.ErrorReport;
import org.apache.doris.common.FeNameFormat;
import org.apache.doris.common.UserException;
import org.apache.doris.mysql.privilege.Auth.PrivLevel;
-import org.apache.doris.mysql.privilege.PrivBitSet;
+import org.apache.doris.mysql.privilege.ColPrivilegeKey;
import org.apache.doris.mysql.privilege.PrivPredicate;
import org.apache.doris.mysql.privilege.Privilege;
import org.apache.doris.qe.ConnectContext;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.MapUtils;
+import java.util.Collection;
import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
// GRANT STMT
-// GRANT privilege [, privilege] ON db.tbl TO user_identity [ROLE 'role'];
+// GRANT privilege[(col1,col2...)] [, privilege] ON db.tbl TO user_identity
[ROLE 'role'];
// GRANT privilege [, privilege] ON RESOURCE 'resource' TO user_identity [ROLE
'role'];
// GRANT role [, role] TO user_identity
public class GrantStmt extends DdlStmt {
@@ -48,21 +55,27 @@ public class GrantStmt extends DdlStmt {
private TablePattern tblPattern;
private ResourcePattern resourcePattern;
private WorkloadGroupPattern workloadGroupPattern;
- private List<Privilege> privileges;
+ private Set<Privilege> privileges = Sets.newHashSet();
+ //Privilege,ctl,db,table -> cols
+ private Map<ColPrivilegeKey, Set<String>> colPrivileges =
Maps.newHashMap();
// Indicates that these roles are granted to a user
private List<String> roles;
+ //AccessPrivileges will be parsed into two parts,
+ // with the column permissions section placed in "colPrivileges" and the
others in "privileges"
+ private List<AccessPrivilegeWithCols> accessPrivileges;
- public GrantStmt(UserIdentity userIdent, String role, TablePattern
tblPattern, List<AccessPrivilege> privileges) {
+ public GrantStmt(UserIdentity userIdent, String role, TablePattern
tblPattern,
+ List<AccessPrivilegeWithCols> privileges) {
this(userIdent, role, tblPattern, null, null, privileges);
}
public GrantStmt(UserIdentity userIdent, String role,
- ResourcePattern resourcePattern, List<AccessPrivilege> privileges)
{
+ ResourcePattern resourcePattern, List<AccessPrivilegeWithCols>
privileges) {
this(userIdent, role, null, resourcePattern, null, privileges);
}
public GrantStmt(UserIdentity userIdent, String role,
- WorkloadGroupPattern workloadGroupPattern, List<AccessPrivilege>
privileges) {
+ WorkloadGroupPattern workloadGroupPattern,
List<AccessPrivilegeWithCols> privileges) {
this(userIdent, role, null, null, workloadGroupPattern, privileges);
}
@@ -72,17 +85,13 @@ public class GrantStmt extends DdlStmt {
}
private GrantStmt(UserIdentity userIdent, String role, TablePattern
tblPattern, ResourcePattern resourcePattern,
- WorkloadGroupPattern workloadGroupPattern, List<AccessPrivilege>
privileges) {
+ WorkloadGroupPattern workloadGroupPattern,
List<AccessPrivilegeWithCols> accessPrivileges) {
this.userIdent = userIdent;
this.role = role;
this.tblPattern = tblPattern;
this.resourcePattern = resourcePattern;
this.workloadGroupPattern = workloadGroupPattern;
- PrivBitSet privs = PrivBitSet.of();
- for (AccessPrivilege accessPrivilege : privileges) {
- privs.or(accessPrivilege.toPaloPrivilege());
- }
- this.privileges = privs.toPrivilegeList();
+ this.accessPrivileges = accessPrivileges;
}
public UserIdentity getUserIdent() {
@@ -109,7 +118,7 @@ public class GrantStmt extends DdlStmt {
return role;
}
- public List<Privilege> getPrivileges() {
+ public Set<Privilege> getPrivileges() {
return privileges;
}
@@ -117,6 +126,10 @@ public class GrantStmt extends DdlStmt {
return roles;
}
+ public Map<ColPrivilegeKey, Set<String>> getColPrivileges() {
+ return colPrivileges;
+ }
+
@Override
public void analyze(Analyzer analyzer) throws UserException {
super.analyze(analyzer);
@@ -141,12 +154,21 @@ public class GrantStmt extends DdlStmt {
}
}
- if (CollectionUtils.isEmpty(privileges) &&
CollectionUtils.isEmpty(roles)) {
+
+ if (!CollectionUtils.isEmpty(accessPrivileges)) {
+ checkAccessPrivileges(accessPrivileges);
+
+ for (AccessPrivilegeWithCols accessPrivilegeWithCols :
accessPrivileges) {
+
accessPrivilegeWithCols.transferAccessPrivilegeToDoris(privileges,
colPrivileges, tblPattern);
+ }
+ }
+
+ if (CollectionUtils.isEmpty(privileges) &&
CollectionUtils.isEmpty(roles) && MapUtils.isEmpty(colPrivileges)) {
throw new AnalysisException("No privileges or roles in grant
statement.");
}
if (tblPattern != null) {
- checkTablePrivileges(privileges, role, tblPattern);
+ checkTablePrivileges(privileges, role, tblPattern, colPrivileges);
} else if (resourcePattern != null) {
checkResourcePrivileges(privileges, role, resourcePattern);
} else if (workloadGroupPattern != null) {
@@ -156,6 +178,16 @@ public class GrantStmt extends DdlStmt {
}
}
+ public static void checkAccessPrivileges(
+ List<AccessPrivilegeWithCols> accessPrivileges) throws
AnalysisException {
+ for (AccessPrivilegeWithCols access : accessPrivileges) {
+ if (!access.getAccessPrivilege().canHasColPriv() &&
!CollectionUtils.isEmpty(access.getCols())) {
+ throw new AnalysisException(
+ String.format("%s do not support col auth.",
access.getAccessPrivilege().name()));
+ }
+ }
+ }
+
/**
* Rules:
* 1. ADMIN_PRIV and NODE_PRIV can only be granted/revoked on GLOBAL level
@@ -173,7 +205,8 @@ public class GrantStmt extends DdlStmt {
* @param tblPattern
* @throws AnalysisException
*/
- public static void checkTablePrivileges(List<Privilege> privileges, String
role, TablePattern tblPattern)
+ public static void checkTablePrivileges(Collection<Privilege> privileges,
String role, TablePattern tblPattern,
+ Map<ColPrivilegeKey, Set<String>> colPrivileges)
throws AnalysisException {
// Rule 1
if (tblPattern.getPrivLevel() != PrivLevel.GLOBAL &&
(privileges.contains(Privilege.ADMIN_PRIV)
@@ -223,9 +256,14 @@ public class GrantStmt extends DdlStmt {
if (privileges.contains(Privilege.USAGE_PRIV)) {
throw new AnalysisException("Can not grant/revoke USAGE_PRIV
to/from database or table");
}
+
+ // Rule 7
+ if (!MapUtils.isEmpty(colPrivileges) &&
"*".equals(tblPattern.getTbl())) {
+ throw new AnalysisException("Col auth must specify specific
table");
+ }
}
- public static void checkResourcePrivileges(List<Privilege> privileges,
String role,
+ public static void checkResourcePrivileges(Collection<Privilege>
privileges, String role,
ResourcePattern resourcePattern) throws AnalysisException {
for (int i = 0; i < Privilege.notBelongToResourcePrivileges.length;
i++) {
if
(privileges.contains(Privilege.notBelongToResourcePrivileges[i])) {
@@ -256,7 +294,7 @@ public class GrantStmt extends DdlStmt {
}
}
- public static void checkWorkloadGroupPrivileges(List<Privilege>
privileges, String role,
+ public static void checkWorkloadGroupPrivileges(Collection<Privilege>
privileges, String role,
WorkloadGroupPattern workloadGroupPattern) throws
AnalysisException {
for (int i = 0; i <
Privilege.notBelongToWorkloadGroupPrivileges.length; i++) {
if
(privileges.contains(Privilege.notBelongToWorkloadGroupPrivileges[i])) {
@@ -283,16 +321,34 @@ public class GrantStmt extends DdlStmt {
}
}
+ public static String colPrivMapToString(Map<ColPrivilegeKey, Set<String>>
colPrivileges) {
+ if (MapUtils.isEmpty(colPrivileges)) {
+ return "";
+ }
+ StringBuilder builder = new StringBuilder();
+ for (Entry<ColPrivilegeKey, Set<String>> entry :
colPrivileges.entrySet()) {
+ builder.append(entry.getKey().getPrivilege());
+ builder.append("(");
+ builder.append(Joiner.on(", ").join(entry.getValue()));
+ builder.append(")");
+ builder.append(",");
+ }
+ return builder.deleteCharAt(builder.length() - 1).toString();
+ }
+
@Override
public String toSql() {
StringBuilder sb = new StringBuilder();
sb.append("GRANT ");
- if (privileges != null) {
+ if (!CollectionUtils.isEmpty(privileges)) {
sb.append(Joiner.on(", ").join(privileges));
- } else {
+ }
+ if (!MapUtils.isEmpty(colPrivileges)) {
+ sb.append(colPrivMapToString(colPrivileges));
+ }
+ if (!CollectionUtils.isEmpty(roles)) {
sb.append(Joiner.on(", ").join(roles));
}
-
if (tblPattern != null) {
sb.append(" ON ").append(tblPattern).append(" TO ");
} else if (resourcePattern != null) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/RevokeStmt.java
b/fe/fe-core/src/main/java/org/apache/doris/analysis/RevokeStmt.java
index e008e9a8a8..74e9a4de91 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/RevokeStmt.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/RevokeStmt.java
@@ -17,23 +17,27 @@
package org.apache.doris.analysis;
-import org.apache.doris.catalog.AccessPrivilege;
+import org.apache.doris.catalog.AccessPrivilegeWithCols;
import org.apache.doris.cluster.ClusterNamespace;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.common.FeNameFormat;
-import org.apache.doris.mysql.privilege.PrivBitSet;
+import org.apache.doris.mysql.privilege.ColPrivilegeKey;
import org.apache.doris.mysql.privilege.Privilege;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.MapUtils;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
// REVOKE STMT
// revoke privilege from some user, this is an administrator operation.
-//
-// REVOKE privilege [, privilege] ON db.tbl FROM user_identity [ROLE 'role'];
+// REVOKE privilege[(col1,col2...)] [, privilege] ON db.tbl FROM user_identity
[ROLE 'role'];
// REVOKE privilege [, privilege] ON resource 'resource' FROM user_identity
[ROLE 'role'];
// REVOKE role [, role] FROM user_identity
public class RevokeStmt extends DdlStmt {
@@ -43,21 +47,24 @@ public class RevokeStmt extends DdlStmt {
private TablePattern tblPattern;
private ResourcePattern resourcePattern;
private WorkloadGroupPattern workloadGroupPattern;
- private List<Privilege> privileges;
+ private Set<Privilege> privileges = Sets.newHashSet();
+ private Map<ColPrivilegeKey, Set<String>> colPrivileges =
Maps.newHashMap();
// Indicates that these roles are revoked from a user
private List<String> roles;
+ List<AccessPrivilegeWithCols> accessPrivileges;
- public RevokeStmt(UserIdentity userIdent, String role, TablePattern
tblPattern, List<AccessPrivilege> privileges) {
+ public RevokeStmt(UserIdentity userIdent, String role, TablePattern
tblPattern,
+ List<AccessPrivilegeWithCols> privileges) {
this(userIdent, role, tblPattern, null, null, privileges);
}
public RevokeStmt(UserIdentity userIdent, String role,
- ResourcePattern resourcePattern, List<AccessPrivilege> privileges)
{
+ ResourcePattern resourcePattern, List<AccessPrivilegeWithCols>
privileges) {
this(userIdent, role, null, resourcePattern, null, privileges);
}
public RevokeStmt(UserIdentity userIdent, String role,
- WorkloadGroupPattern workloadGroupPattern, List<AccessPrivilege>
privileges) {
+ WorkloadGroupPattern workloadGroupPattern,
List<AccessPrivilegeWithCols> privileges) {
this(userIdent, role, null, null, workloadGroupPattern, privileges);
}
@@ -67,17 +74,13 @@ public class RevokeStmt extends DdlStmt {
}
private RevokeStmt(UserIdentity userIdent, String role, TablePattern
tblPattern, ResourcePattern resourcePattern,
- WorkloadGroupPattern workloadGroupPattern, List<AccessPrivilege>
privileges) {
+ WorkloadGroupPattern workloadGroupPattern,
List<AccessPrivilegeWithCols> accessPrivileges) {
this.userIdent = userIdent;
this.role = role;
this.tblPattern = tblPattern;
this.resourcePattern = resourcePattern;
this.workloadGroupPattern = workloadGroupPattern;
- PrivBitSet privs = PrivBitSet.of();
- for (AccessPrivilege accessPrivilege : privileges) {
- privs.or(accessPrivilege.toPaloPrivilege());
- }
- this.privileges = privs.toPrivilegeList();
+ this.accessPrivileges = accessPrivileges;
}
public UserIdentity getUserIdent() {
@@ -100,7 +103,7 @@ public class RevokeStmt extends DdlStmt {
return role;
}
- public List<Privilege> getPrivileges() {
+ public Set<Privilege> getPrivileges() {
return privileges;
}
@@ -108,6 +111,10 @@ public class RevokeStmt extends DdlStmt {
return roles;
}
+ public Map<ColPrivilegeKey, Set<String>> getColPrivileges() {
+ return colPrivileges;
+ }
+
@Override
public void analyze(Analyzer analyzer) throws AnalysisException {
if (userIdent != null) {
@@ -130,14 +137,20 @@ public class RevokeStmt extends DdlStmt {
roles.set(i,
ClusterNamespace.getFullName(analyzer.getClusterName(), originalRoleName));
}
}
+ if (!CollectionUtils.isEmpty(accessPrivileges)) {
+ GrantStmt.checkAccessPrivileges(accessPrivileges);
- if (CollectionUtils.isEmpty(privileges) &&
CollectionUtils.isEmpty(roles)) {
+ for (AccessPrivilegeWithCols accessPrivilegeWithCols :
accessPrivileges) {
+
accessPrivilegeWithCols.transferAccessPrivilegeToDoris(privileges,
colPrivileges, tblPattern);
+ }
+ }
+ if (CollectionUtils.isEmpty(privileges) &&
CollectionUtils.isEmpty(roles) && MapUtils.isEmpty(colPrivileges)) {
throw new AnalysisException("No privileges or roles in revoke
statement.");
}
// Revoke operation obey the same rule as Grant operation. reuse the
same method
if (tblPattern != null) {
- GrantStmt.checkTablePrivileges(privileges, role, tblPattern);
+ GrantStmt.checkTablePrivileges(privileges, role, tblPattern,
colPrivileges);
} else if (resourcePattern != null) {
GrantStmt.checkResourcePrivileges(privileges, role,
resourcePattern);
} else if (workloadGroupPattern != null) {
@@ -151,9 +164,13 @@ public class RevokeStmt extends DdlStmt {
public String toSql() {
StringBuilder sb = new StringBuilder();
sb.append("REVOKE ");
- if (privileges != null) {
+ if (!CollectionUtils.isEmpty(privileges)) {
sb.append(Joiner.on(", ").join(privileges));
- } else {
+ }
+ if (!MapUtils.isEmpty(colPrivileges)) {
+ sb.append(GrantStmt.colPrivMapToString(colPrivileges));
+ }
+ if (!CollectionUtils.isEmpty(roles)) {
sb.append(Joiner.on(", ").join(roles));
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/catalog/AccessPrivilege.java
b/fe/fe-core/src/main/java/org/apache/doris/catalog/AccessPrivilege.java
index 246805f08a..88c6c9649b 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/AccessPrivilege.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/AccessPrivilege.java
@@ -17,10 +17,10 @@
package org.apache.doris.catalog;
-import org.apache.doris.mysql.privilege.PrivBitSet;
import org.apache.doris.mysql.privilege.Privilege;
import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
import java.util.List;
@@ -48,39 +48,43 @@ public enum AccessPrivilege {
this.desc = desc;
}
- public PrivBitSet toPaloPrivilege() {
+ public List<Privilege> toDorisPrivilege() {
Preconditions.checkState(flag > 0 && flag < 13);
switch (flag) {
case 1:
- return PrivBitSet.of(Privilege.SELECT_PRIV);
+ case 6:
+ return Lists.newArrayList(Privilege.SELECT_PRIV);
case 2:
case 3:
- return PrivBitSet.of(Privilege.SELECT_PRIV,
Privilege.LOAD_PRIV,
+ return Lists.newArrayList(Privilege.SELECT_PRIV,
Privilege.LOAD_PRIV,
Privilege.ALTER_PRIV, Privilege.CREATE_PRIV,
Privilege.DROP_PRIV);
case 4:
- return PrivBitSet.of(Privilege.NODE_PRIV);
+ return Lists.newArrayList(Privilege.NODE_PRIV);
case 5:
- return PrivBitSet.of(Privilege.GRANT_PRIV);
- case 6:
- return PrivBitSet.of(Privilege.SELECT_PRIV);
+ return Lists.newArrayList(Privilege.GRANT_PRIV);
case 7:
- return PrivBitSet.of(Privilege.LOAD_PRIV);
+ return Lists.newArrayList(Privilege.LOAD_PRIV);
case 8:
- return PrivBitSet.of(Privilege.ALTER_PRIV);
+ return Lists.newArrayList(Privilege.ALTER_PRIV);
case 9:
- return PrivBitSet.of(Privilege.CREATE_PRIV);
+ return Lists.newArrayList(Privilege.CREATE_PRIV);
case 10:
- return PrivBitSet.of(Privilege.DROP_PRIV);
+ return Lists.newArrayList(Privilege.DROP_PRIV);
case 11:
- return PrivBitSet.of(Privilege.ADMIN_PRIV);
+ return Lists.newArrayList(Privilege.ADMIN_PRIV);
case 12:
- return PrivBitSet.of(Privilege.USAGE_PRIV);
+ return Lists.newArrayList(Privilege.USAGE_PRIV);
default:
return null;
}
}
+ // Used to restrict which permissions support column permissions
+ public boolean canHasColPriv() {
+ return this == SELECT_PRIV;
+ }
+
public static AccessPrivilege fromName(String privStr) {
try {
return AccessPrivilege.valueOf(privStr.toUpperCase());
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/catalog/AccessPrivilegeWithCols.java
b/fe/fe-core/src/main/java/org/apache/doris/catalog/AccessPrivilegeWithCols.java
new file mode 100644
index 0000000000..9866949ecf
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/catalog/AccessPrivilegeWithCols.java
@@ -0,0 +1,87 @@
+// 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.catalog;
+
+import org.apache.doris.analysis.TablePattern;
+import org.apache.doris.mysql.privilege.ColPrivilegeKey;
+import org.apache.doris.mysql.privilege.Privilege;
+
+import com.google.common.collect.Sets;
+import org.apache.commons.collections.CollectionUtils;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class AccessPrivilegeWithCols {
+ private AccessPrivilege accessPrivilege;
+ private List<String> cols;
+
+ public AccessPrivilegeWithCols(AccessPrivilege accessPrivilege,
List<String> cols) {
+ this.accessPrivilege = accessPrivilege;
+ this.cols = cols;
+ }
+
+ public AccessPrivilegeWithCols(AccessPrivilege accessPrivilege) {
+ this.accessPrivilege = accessPrivilege;
+ }
+
+ public AccessPrivilege getAccessPrivilege() {
+ return accessPrivilege;
+ }
+
+ public void setAccessPrivilege(AccessPrivilege accessPrivilege) {
+ this.accessPrivilege = accessPrivilege;
+ }
+
+ public List<String> getCols() {
+ return cols;
+ }
+
+ public void setCols(List<String> cols) {
+ this.cols = cols;
+ }
+
+ @Override
+ public String toString() {
+ return "AccessPrivilegeWithCols{"
+ + "accessPrivilege=" + accessPrivilege
+ + ", cols=" + cols
+ + '}';
+ }
+
+ public void transferAccessPrivilegeToDoris(Set<Privilege> privileges,
+ Map<ColPrivilegeKey, Set<String>> colPrivileges, TablePattern
tblPattern) {
+ List<Privilege> dorisPrivileges = accessPrivilege.toDorisPrivilege();
+ // if has no cols,represents the permissions assigned to the entire
table
+ if (CollectionUtils.isEmpty(cols)) {
+ privileges.addAll(dorisPrivileges);
+ } else {
+ // if has cols, represents the permissions assigned to the cols
+ for (Privilege privilege : dorisPrivileges) {
+ ColPrivilegeKey colPrivilegeKey = new
ColPrivilegeKey(privilege, tblPattern.getQualifiedCtl(),
+ tblPattern.getQualifiedDb(), tblPattern.getTbl());
+ if (colPrivileges.containsKey(colPrivilegeKey)) {
+ colPrivileges.get(colPrivilegeKey).addAll(cols);
+ } else {
+ colPrivileges.put(colPrivilegeKey, Sets.newHashSet(cols));
+ }
+ }
+ }
+ }
+}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/common/proc/AuthProcDir.java
b/fe/fe-core/src/main/java/org/apache/doris/common/proc/AuthProcDir.java
index e4cc0c96ec..6adc86ee26 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/common/proc/AuthProcDir.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/common/proc/AuthProcDir.java
@@ -32,7 +32,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("Roles").add("GlobalPrivs").add("CatalogPrivs")
-
.add("DatabasePrivs").add("TablePrivs").add("ResourcePrivs").add("WorkloadGroupPrivs").build();
+
.add("DatabasePrivs").add("TablePrivs").add("ColPrivs").add("ResourcePrivs").add("WorkloadGroupPrivs")
+ .build();
private Auth auth;
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Auth.java
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Auth.java
index 274ccb56bf..6e249516f2 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Auth.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Auth.java
@@ -67,6 +67,7 @@ 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.Maps;
import com.google.common.collect.Sets;
import org.apache.commons.collections.CollectionUtils;
import org.apache.logging.log4j.LogManager;
@@ -332,17 +333,29 @@ public class Auth implements Writable {
}
// ==== Column ====
+ // The reason why this method throws an exception instead of returning a
boolean is to
+ // indicate which col does not have permission
public void checkColsPriv(UserIdentity currentUser, String ctl, String db,
String tbl, Set<String> cols,
PrivPredicate wanted) throws AuthorizationException {
- // TODO: Support column priv
- // we check if have tbl priv,until internal support col auth.
- if (!checkTblPriv(currentUser, ctl, db, tbl, wanted)) {
- throw new AuthorizationException(String.format(
- "Permission denied: user [%s] does not have privilege for
[%s] command on [%s].[%s].[%s]",
- currentUser, wanted, ctl, db, tbl));
+ Set<Role> roles = getRolesByUserWithLdap(currentUser);
+ for (String col : cols) {
+ if (!checkColPriv(ctl, db, tbl, col, wanted, roles)) {
+ throw new AuthorizationException(String.format(
+ "Permission denied: user [%s] does not have privilege
for [%s] command on [%s].[%s].[%s].[%s]",
+ currentUser, wanted, ctl, db, tbl, col));
+ }
}
}
+ private boolean checkColPriv(String ctl, String db, String tbl,
+ String col, PrivPredicate wanted, Set<Role> roles) {
+ for (Role role : roles) {
+ if (role.checkColPriv(ctl, db, tbl, col, wanted)) {
+ return true;
+ }
+ }
+ return false;
+ }
// ==== Resource ====
public boolean checkResourcePriv(UserIdentity currentUser, String
resourceName, PrivPredicate wanted) {
@@ -520,7 +533,7 @@ public class Auth implements Writable {
if (stmt.getTblPattern() != null) {
PrivBitSet privs = PrivBitSet.of(stmt.getPrivileges());
grantInternal(stmt.getUserIdent(), stmt.getQualifiedRole(),
stmt.getTblPattern(), privs,
- true /* err on non exist */, false /* not replay */);
+ stmt.getColPrivileges(), true /* err on non exist */,
false /* not replay */);
} else if (stmt.getResourcePattern() != null) {
PrivBitSet privs = PrivBitSet.of(stmt.getPrivileges());
grantInternal(stmt.getUserIdent(), stmt.getQualifiedRole(),
stmt.getResourcePattern(), privs,
@@ -538,7 +551,7 @@ public class Auth implements Writable {
try {
if (privInfo.getTblPattern() != null) {
grantInternal(privInfo.getUserIdent(), privInfo.getRole(),
- privInfo.getTblPattern(), privInfo.getPrivs(),
+ privInfo.getTblPattern(), privInfo.getPrivs(),
privInfo.getColPrivileges(),
true /* err on non exist */, true /* is replay */);
} else if (privInfo.getResourcePattern() != null) {
grantInternal(privInfo.getUserIdent(), privInfo.getRole(),
@@ -558,8 +571,8 @@ public class Auth implements Writable {
// grant for TablePattern
//if no role,role is default role of userIdent
- private void grantInternal(UserIdentity userIdent, String role,
TablePattern tblPattern,
- PrivBitSet privs, boolean errOnNonExist, boolean isReplay)
+ private void grantInternal(UserIdentity userIdent, String role,
TablePattern tblPattern, PrivBitSet privs,
+ Map<ColPrivilegeKey, Set<String>> colPrivileges, boolean
errOnNonExist, boolean isReplay)
throws DdlException {
writeLock();
try {
@@ -569,10 +582,10 @@ public class Auth implements Writable {
}
role = roleManager.getUserDefaultRoleName(userIdent);
}
- Role newRole = new Role(role, tblPattern, privs);
+ Role newRole = new Role(role, tblPattern, privs, colPrivileges);
roleManager.addOrMergeRole(newRole, false /* err on exist */);
if (!isReplay) {
- PrivInfo info = new PrivInfo(userIdent, tblPattern, privs,
null, role);
+ PrivInfo info = new PrivInfo(userIdent, tblPattern, privs,
null, role, colPrivileges);
Env.getCurrentEnv().getEditLog().logGrantPriv(info);
}
LOG.info("finished to grant privilege. is replay: {}", isReplay);
@@ -672,7 +685,7 @@ public class Auth implements Writable {
if (stmt.getTblPattern() != null) {
PrivBitSet privs = PrivBitSet.of(stmt.getPrivileges());
revokeInternal(stmt.getUserIdent(), stmt.getQualifiedRole(),
stmt.getTblPattern(), privs,
- true /* err on non exist */, false /* is replay */);
+ stmt.getColPrivileges(), true /* err on non exist */,
false /* is replay */);
} else if (stmt.getResourcePattern() != null) {
PrivBitSet privs = PrivBitSet.of(stmt.getPrivileges());
revokeInternal(stmt.getUserIdent(), stmt.getQualifiedRole(),
stmt.getResourcePattern(), privs,
@@ -690,7 +703,7 @@ public class Auth implements Writable {
try {
if (info.getTblPattern() != null) {
revokeInternal(info.getUserIdent(), info.getRole(),
info.getTblPattern(), info.getPrivs(),
- true /* err on non exist */, true /* is replay */);
+ info.getColPrivileges(), true /* err on non exist */,
true /* is replay */);
} else if (info.getResourcePattern() != null) {
revokeInternal(info.getUserIdent(), info.getRole(),
info.getResourcePattern(), info.getPrivs(),
true /* err on non exist */, true /* is replay */);
@@ -706,17 +719,18 @@ public class Auth implements Writable {
}
private void revokeInternal(UserIdentity userIdent, String role,
TablePattern tblPattern,
- PrivBitSet privs, boolean errOnNonExist, boolean isReplay) throws
DdlException {
+ PrivBitSet privs, Map<ColPrivilegeKey, Set<String>> colPrivileges,
boolean errOnNonExist, boolean isReplay)
+ throws DdlException {
writeLock();
try {
if (role == null) {
role = roleManager.getUserDefaultRoleName(userIdent);
}
// revoke privs from role
- roleManager.revokePrivs(role, tblPattern, privs, errOnNonExist);
+ roleManager.revokePrivs(role, tblPattern, privs, colPrivileges,
errOnNonExist);
if (!isReplay) {
- PrivInfo info = new PrivInfo(userIdent, tblPattern, privs,
null, role);
+ PrivInfo info = new PrivInfo(userIdent, tblPattern, privs,
null, role, colPrivileges);
Env.getCurrentEnv().getEditLog().logRevokePriv(info);
}
LOG.info("finished to revoke privilege. is replay: {}", isReplay);
@@ -1155,6 +1169,19 @@ public class Auth implements Writable {
userAuthInfo.add(Joiner.on("; ").join(tblPrivs));
}
+ // col
+ List<String> colPrivs = Lists.newArrayList();
+ for (Entry<ColPrivilegeKey, Set<String>> entry :
getUserColPrivMap(userIdent).entrySet()) {
+ colPrivs.add(String.format("%s.%s.%s: %s%s",
entry.getKey().getCtl(), entry.getKey().getDb(),
+ entry.getKey().getTbl(), entry.getKey().getPrivilege(),
entry.getValue()));
+ }
+
+ if (colPrivs.isEmpty()) {
+ userAuthInfo.add(FeConstants.null_string);
+ } else {
+ userAuthInfo.add(Joiner.on("; ").join(colPrivs));
+ }
+
// resource
List<String> resourcePrivs = Lists.newArrayList();
for (PrivEntry entry : getUserResourcePrivTable(userIdent).entries) {
@@ -1222,6 +1249,16 @@ public class Auth implements Writable {
return table;
}
+ private Map<ColPrivilegeKey, Set<String>> getUserColPrivMap(UserIdentity
userIdentity) {
+ Map<ColPrivilegeKey, Set<String>> colPrivMap = Maps.newHashMap();
+ Set<Role> roles = getRolesByUserWithLdap(userIdentity);
+ for (Role role : roles) {
+ Role.mergeColPrivMap(colPrivMap, role.getColPrivMap());
+ }
+ return colPrivMap;
+ }
+
+
private ResourcePrivTable getUserResourcePrivTable(UserIdentity
userIdentity) {
ResourcePrivTable table = new ResourcePrivTable();
Set<Role> roles = getRolesByUserWithLdap(userIdentity);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/ColPrivilegeKey.java
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/ColPrivilegeKey.java
new file mode 100644
index 0000000000..9d4b791cb0
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/ColPrivilegeKey.java
@@ -0,0 +1,98 @@
+// 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.cluster.ClusterNamespace;
+
+import com.google.common.base.Objects;
+import com.google.gson.annotations.SerializedName;
+
+public class ColPrivilegeKey {
+
+ @SerializedName(value = "privilegeIdx")
+ private int privilegeIdx;
+ @SerializedName(value = "ctl")
+ private String ctl;
+ @SerializedName(value = "db")
+ private String db;
+ @SerializedName(value = "tbl")
+ private String tbl;
+
+ public ColPrivilegeKey(Privilege privilege, String ctl, String db, String
tbl) {
+ this.privilegeIdx = privilege.getIdx();
+ this.ctl = ClusterNamespace.getNameFromFullName(ctl);
+ this.db = ClusterNamespace.getNameFromFullName(db);
+ this.tbl = ClusterNamespace.getNameFromFullName(tbl);
+ }
+
+ public Privilege getPrivilege() {
+ return Privilege.getPriv(privilegeIdx);
+ }
+
+ public int getPrivilegeIdx() {
+ return privilegeIdx;
+ }
+
+ public void setPrivilegeIdx(int privilegeIdx) {
+ this.privilegeIdx = privilegeIdx;
+ }
+
+ public String getCtl() {
+ return ctl;
+ }
+
+ public void setCtl(String ctl) {
+ this.ctl = ctl;
+ }
+
+ public String getDb() {
+ return db;
+ }
+
+ public void setDb(String db) {
+ this.db = db;
+ }
+
+ public String getTbl() {
+ return tbl;
+ }
+
+ public void setTbl(String tbl) {
+ this.tbl = tbl;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ColPrivilegeKey that = (ColPrivilegeKey) o;
+ return privilegeIdx == that.privilegeIdx
+ && Objects.equal(ctl, that.ctl)
+ && Objects.equal(db, that.db)
+ && Objects.equal(tbl, that.tbl);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(privilegeIdx, ctl, db, tbl);
+ }
+}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/PrivBitSet.java
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/PrivBitSet.java
index 940a331fa9..f06646526e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/PrivBitSet.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/PrivBitSet.java
@@ -31,6 +31,7 @@ import com.google.gson.annotations.SerializedName;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
+import java.util.Collection;
import java.util.List;
// ....0000000000
@@ -135,7 +136,7 @@ public class PrivBitSet implements Writable {
return bitSet;
}
- public static PrivBitSet of(List<Privilege> privs) {
+ public static PrivBitSet of(Collection<Privilege> privs) {
PrivBitSet bitSet = new PrivBitSet();
for (Privilege priv : privs) {
bitSet.set(priv.getIdx());
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/PrivPredicate.java
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/PrivPredicate.java
index 6e8916db2d..61f820f674 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/PrivPredicate.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/PrivPredicate.java
@@ -19,6 +19,8 @@ package org.apache.doris.mysql.privilege;
import org.apache.doris.analysis.CompoundPredicate.Operator;
+import java.util.Optional;
+
public class PrivPredicate {
// user can 'see' this meta
@@ -34,7 +36,7 @@ public class PrivPredicate {
Privilege.USAGE_PRIV),
Operator.OR);
public static final PrivPredicate SHOW_WORKLOAD_GROUP =
PrivPredicate.of(PrivBitSet.of(Privilege.ADMIN_PRIV,
- Privilege.USAGE_PRIV),
+ Privilege.USAGE_PRIV),
Operator.OR);
// create/drop/alter/show user
public static final PrivPredicate GRANT =
PrivPredicate.of(PrivBitSet.of(Privilege.ADMIN_PRIV,
@@ -110,6 +112,18 @@ public class PrivPredicate {
return op;
}
+ // Determine which column Privilege correspond to PrivPredicate
+ //The current logic is to include a SELECT_ PRIV returns SELECT_ PRIV, if
load is included_ PRIV returns LOAD_ PRIV,
+ // the order cannot be reversed
+ public Optional<Privilege> getColPrivilege() {
+ if (privs.get(Privilege.SELECT_PRIV.getIdx())) {
+ return Optional.of(Privilege.SELECT_PRIV);
+ } else if (privs.get(Privilege.LOAD_PRIV.getIdx())) {
+ return Optional.of(Privilege.LOAD_PRIV);
+ }
+ return Optional.empty();
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Role.java
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Role.java
index a4cb457f18..025b86fa63 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Role.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Role.java
@@ -22,6 +22,7 @@ import org.apache.doris.analysis.TablePattern;
import org.apache.doris.analysis.UserIdentity;
import org.apache.doris.analysis.WorkloadGroupPattern;
import org.apache.doris.catalog.Env;
+import org.apache.doris.cluster.ClusterNamespace;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.common.DdlException;
import org.apache.doris.common.FeMetaVersion;
@@ -37,6 +38,7 @@ import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.gson.annotations.SerializedName;
+import org.apache.commons.collections.CollectionUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -45,6 +47,8 @@ import java.io.DataOutput;
import java.io.IOException;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
public class Role implements Writable, GsonPostProcessable {
@@ -80,6 +84,8 @@ public class Role implements Writable, GsonPostProcessable {
private Map<ResourcePattern, PrivBitSet> resourcePatternToPrivs =
Maps.newConcurrentMap();
@SerializedName(value = "workloadGroupPatternToPrivs")
private Map<WorkloadGroupPattern, PrivBitSet> workloadGroupPatternToPrivs
= Maps.newConcurrentMap();
+ @SerializedName(value = "colPrivMap")
+ private Map<ColPrivilegeKey, Set<String>> colPrivMap = Maps.newHashMap();
// Will not be persisted, generated by tblPatternToPrivs and
resourcePatternToPrivs
private GlobalPrivTable globalPrivTable = new GlobalPrivTable();
@@ -89,6 +95,7 @@ public class Role implements Writable, GsonPostProcessable {
private ResourcePrivTable resourcePrivTable = new ResourcePrivTable();
private WorkloadGroupPrivTable workloadGroupPrivTable = new
WorkloadGroupPrivTable();
+
@Deprecated
private Set<UserIdentity> users = Sets.newConcurrentHashSet();
@@ -111,6 +118,14 @@ public class Role implements Writable, GsonPostProcessable
{
grantPrivs(tablePattern, privs.copy());
}
+ public Role(String roleName, TablePattern tablePattern, PrivBitSet privs,
+ Map<ColPrivilegeKey, Set<String>> colPrivileges) throws
DdlException {
+ this.roleName = roleName;
+ this.tblPatternToPrivs.put(tablePattern, privs);
+ grantPrivs(tablePattern, privs.copy());
+ grantCols(colPrivileges);
+ }
+
public Role(String roleName, ResourcePattern resourcePattern, PrivBitSet
privs) throws DdlException {
this.roleName = roleName;
this.resourcePatternToPrivs.put(resourcePattern, privs);
@@ -186,6 +201,18 @@ public class Role implements Writable, GsonPostProcessable
{
}
grantPrivs(entry.getKey(), entry.getValue().copy());
}
+ mergeColPrivMap(colPrivMap, other.colPrivMap);
+ }
+
+ public static void mergeColPrivMap(Map<ColPrivilegeKey, Set<String>>
toColPrivMap,
+ Map<ColPrivilegeKey, Set<String>> fromColPrivMap) {
+ for (Entry<ColPrivilegeKey, Set<String>> entry :
fromColPrivMap.entrySet()) {
+ if (toColPrivMap.containsKey(entry.getKey())) {
+ toColPrivMap.get(entry.getKey()).addAll(entry.getValue());
+ } else {
+ toColPrivMap.put(entry.getKey(), entry.getValue());
+ }
+ }
}
public void merge(Role other) throws DdlException {
@@ -227,7 +254,8 @@ public class Role implements Writable, GsonPostProcessable {
*/
private boolean checkAnyPrivWithinCatalog(String ctl) {
return dbPrivTable.hasPrivsOfCatalog(ctl)
- || tablePrivTable.hasPrivsOfCatalog(ctl);
+ || tablePrivTable.hasPrivsOfCatalog(ctl)
+ || checkAnyColPrivWithinCtl(ctl);
}
@@ -263,8 +291,42 @@ public class Role implements Writable, GsonPostProcessable
{
* if so, the database should be visible to this user.
*/
private boolean checkAnyPrivWithinDb(String ctl, String db) {
- return tablePrivTable.hasPrivsOfDb(ctl, db);
+ return tablePrivTable.hasPrivsOfDb(ctl, db) ||
checkAnyColPrivWithinDb(ctl, db);
+
+ }
+
+ private boolean checkAnyColPrivWithinCtl(String ctl) {
+ ctl = ClusterNamespace.getNameFromFullName(ctl);
+ for (ColPrivilegeKey colPrivilegeKey : colPrivMap.keySet()) {
+ if (colPrivilegeKey.getCtl().equals(ctl)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ private boolean checkAnyColPrivWithinDb(String ctl, String db) {
+ ctl = ClusterNamespace.getNameFromFullName(ctl);
+ db = ClusterNamespace.getNameFromFullName(db);
+ for (ColPrivilegeKey colPrivilegeKey : colPrivMap.keySet()) {
+ if (colPrivilegeKey.getCtl().equals(ctl) &&
colPrivilegeKey.getDb().equals(db)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean checkAnyColPrivWithinTbl(String ctl, String db, String
tbl) {
+ ctl = ClusterNamespace.getNameFromFullName(ctl);
+ db = ClusterNamespace.getNameFromFullName(db);
+ tbl = ClusterNamespace.getNameFromFullName(tbl);
+ for (ColPrivilegeKey colPrivilegeKey : colPrivMap.keySet()) {
+ if (colPrivilegeKey.getCtl().equals(ctl) &&
colPrivilegeKey.getDb().equals(db) && colPrivilegeKey
+ .getTbl().equals(tbl)) {
+ return true;
+ }
+ }
+ return false;
}
private boolean checkDbInternal(String ctl, String db, PrivPredicate
wanted,
@@ -284,10 +346,32 @@ public class Role implements Writable,
GsonPostProcessable {
|| checkTblInternal(ctl, db, tbl, wanted, savedPrivs)) {
return true;
}
+
+ // if user has any privs of col in this table, and the wanted priv is
SHOW, return true
+ if (ctl != null && db != null && tbl != null && wanted ==
PrivPredicate.SHOW && checkAnyColPrivWithinTbl(ctl,
+ db, tbl)) {
+ return true;
+ }
+
LOG.debug("failed to get wanted privs: {}, granted: {}", wanted,
savedPrivs);
return false;
}
+ public boolean checkColPriv(String ctl, String db, String tbl, String col,
PrivPredicate wanted) {
+ Optional<Privilege> colPrivilege = wanted.getColPrivilege();
+ Preconditions.checkState(colPrivilege.isPresent(), "this privPredicate
should not use checkColPriv:" + wanted);
+ return checkTblPriv(ctl, db, tbl, wanted) || onlyCheckColPriv(ctl, db,
tbl, col, colPrivilege.get());
+ }
+
+ private boolean onlyCheckColPriv(String ctl, String db, String tbl, String
col,
+ Privilege colPrivilege) {
+ ColPrivilegeKey colPrivilegeKey = new ColPrivilegeKey(colPrivilege,
ctl, db, tbl);
+ if (colPrivMap.containsKey(colPrivilegeKey)) {
+ return colPrivMap.get(colPrivilegeKey).contains(col);
+ }
+ return false;
+ }
+
private boolean checkTblInternal(String ctl, String db, String tbl,
PrivPredicate wanted, PrivBitSet savedPrivs) {
tablePrivTable.getPrivs(ctl, db, tbl, savedPrivs);
return Privilege.satisfy(savedPrivs, wanted);
@@ -371,6 +455,10 @@ public class Role implements Writable, GsonPostProcessable
{
return tablePrivTable;
}
+ public Map<ColPrivilegeKey, Set<String>> getColPrivMap() {
+ return colPrivMap;
+ }
+
public ResourcePrivTable getResourcePrivTable() {
return resourcePrivTable;
}
@@ -396,7 +484,9 @@ public class Role implements Writable, GsonPostProcessable {
private void grantPrivs(ResourcePattern resourcePattern, PrivBitSet privs)
throws DdlException {
-
+ if (privs.isEmpty()) {
+ return;
+ }
// grant privs to user
switch (resourcePattern.getPrivLevel()) {
case GLOBAL:
@@ -412,12 +502,31 @@ public class Role implements Writable,
GsonPostProcessable {
}
private void grantPrivs(WorkloadGroupPattern workloadGroupPattern,
PrivBitSet privs) throws DdlException {
+ if (privs.isEmpty()) {
+ return;
+ }
if
(workloadGroupPattern.getPrivLevel().equals(PrivLevel.WORKLOAD_GROUP)) {
grantWorkloadGroupPrivs(workloadGroupPattern.getworkloadGroupName(), privs);
}
}
+ private void grantCols(Map<ColPrivilegeKey, Set<String>> colPrivileges) {
+ if (Objects.isNull(colPrivileges)) {
+ return;
+ }
+ for (Entry<ColPrivilegeKey, Set<String>> entry :
colPrivileges.entrySet()) {
+ if (colPrivMap.containsKey(entry.getKey())) {
+ colPrivMap.get(entry.getKey()).addAll(entry.getValue());
+ } else {
+ colPrivMap.put(entry.getKey(),
Sets.newHashSet(entry.getValue()));
+ }
+ }
+ }
+
private void grantPrivs(TablePattern tblPattern, PrivBitSet privs) throws
DdlException {
+ if (privs.isEmpty()) {
+ return;
+ }
// grant privs to user
switch (tblPattern.getPrivLevel()) {
case GLOBAL:
@@ -501,7 +610,9 @@ public class Role implements Writable, GsonPostProcessable {
workloadGroupPrivTable.addEntry(entry, false, false);
}
- public void revokePrivs(TablePattern tblPattern, PrivBitSet privs, boolean
errOnNonExist) throws DdlException {
+ public void revokePrivs(TablePattern tblPattern, PrivBitSet privs,
Map<ColPrivilegeKey, Set<String>> colPrivileges,
+ boolean errOnNonExist)
+ throws DdlException {
PrivBitSet existingPriv = tblPatternToPrivs.get(tblPattern);
if (existingPriv == null) {
if (errOnNonExist) {
@@ -511,6 +622,21 @@ public class Role implements Writable, GsonPostProcessable
{
}
existingPriv.remove(privs);
revokePrivs(tblPattern, privs);
+ revokeCols(colPrivileges);
+ }
+
+ private void revokeCols(Map<ColPrivilegeKey, Set<String>> colPrivileges) {
+ if (Objects.isNull(colPrivileges)) {
+ return;
+ }
+ for (Entry<ColPrivilegeKey, Set<String>> entry :
colPrivileges.entrySet()) {
+ if (colPrivMap.containsKey(entry.getKey())) {
+ colPrivMap.get(entry.getKey()).removeAll(entry.getValue());
+ if (CollectionUtils.isEmpty(colPrivMap.get(entry.getKey()))) {
+ colPrivMap.remove(entry.getKey());
+ }
+ }
+ }
}
public void revokePrivs(ResourcePattern resourcePattern, PrivBitSet privs,
boolean errOnNonExist)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/RoleManager.java
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/RoleManager.java
index 285534d297..7b37b6f40e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/RoleManager.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/RoleManager.java
@@ -51,6 +51,7 @@ import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -101,7 +102,8 @@ public class RoleManager implements Writable {
roles.remove(qualifiedRole);
}
- public Role revokePrivs(String name, TablePattern tblPattern, PrivBitSet
privs, boolean errOnNonExist)
+ public Role revokePrivs(String name, TablePattern tblPattern, PrivBitSet
privs,
+ Map<ColPrivilegeKey, Set<String>> colPrivileges, boolean
errOnNonExist)
throws DdlException {
Role existingRole = roles.get(name);
if (existingRole == null) {
@@ -110,7 +112,7 @@ public class RoleManager implements Writable {
}
return null;
}
- existingRole.revokePrivs(tblPattern, privs, errOnNonExist);
+ existingRole.revokePrivs(tblPattern, privs, colPrivileges,
errOnNonExist);
return existingRole;
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/persist/PrivInfo.java
b/fe/fe-core/src/main/java/org/apache/doris/persist/PrivInfo.java
index 1b6e448cc8..377356d85c 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/persist/PrivInfo.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/persist/PrivInfo.java
@@ -26,6 +26,7 @@ import org.apache.doris.catalog.Env;
import org.apache.doris.common.FeMetaVersion;
import org.apache.doris.common.io.Text;
import org.apache.doris.common.io.Writable;
+import org.apache.doris.mysql.privilege.ColPrivilegeKey;
import org.apache.doris.mysql.privilege.PrivBitSet;
import org.apache.doris.persist.gson.GsonUtils;
@@ -35,6 +36,8 @@ import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
public class PrivInfo implements Writable {
@SerializedName(value = "userIdent")
@@ -51,6 +54,8 @@ public class PrivInfo implements Writable {
private byte[] passwd;
@SerializedName(value = "role")
private String role;
+ @SerializedName(value = "colPrivileges")
+ private Map<ColPrivilegeKey, Set<String>> colPrivileges;
@SerializedName(value = "passwordOptions")
private PasswordOptions passwordOptions;
// Indicates that these roles are granted to a user
@@ -75,7 +80,7 @@ public class PrivInfo implements Writable {
// For grant/revoke
public PrivInfo(UserIdentity userIdent, TablePattern tablePattern,
PrivBitSet privs,
- byte[] passwd, String role) {
+ byte[] passwd, String role, Map<ColPrivilegeKey, Set<String>>
colPrivileges) {
this.userIdent = userIdent;
this.tblPattern = tablePattern;
this.resourcePattern = null;
@@ -83,6 +88,7 @@ public class PrivInfo implements Writable {
this.privs = privs;
this.passwd = passwd;
this.role = role;
+ this.colPrivileges = colPrivileges;
}
// For grant/revoke resource priv
@@ -150,6 +156,10 @@ public class PrivInfo implements Writable {
return roles;
}
+ public Map<ColPrivilegeKey, Set<String>> getColPrivileges() {
+ return colPrivileges;
+ }
+
public static PrivInfo read(DataInput in) throws IOException {
if (Env.getCurrentEnvJournalVersion() < FeMetaVersion.VERSION_113) {
PrivInfo info = new PrivInfo();
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/analysis/GrantStmtTest.java
b/fe/fe-core/src/test/java/org/apache/doris/analysis/GrantStmtTest.java
index bead7dae56..37bf2c8bfb 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/analysis/GrantStmtTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/analysis/GrantStmtTest.java
@@ -18,6 +18,7 @@
package org.apache.doris.analysis;
import org.apache.doris.catalog.AccessPrivilege;
+import org.apache.doris.catalog.AccessPrivilegeWithCols;
import org.apache.doris.catalog.Env;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.common.UserException;
@@ -83,13 +84,13 @@ public class GrantStmtTest {
public void testNormal() throws AnalysisException, UserException {
GrantStmt stmt;
- List<AccessPrivilege> privileges =
Lists.newArrayList(AccessPrivilege.ALL);
+ List<AccessPrivilegeWithCols> privileges = Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.ALL));
stmt = new GrantStmt(new UserIdentity("testUser", "%"), null, new
TablePattern("testDb", "*"), privileges);
stmt.analyze(analyzer);
Assert.assertEquals("testCluster:testUser",
stmt.getUserIdent().getQualifiedUser());
Assert.assertEquals("testCluster:testDb",
stmt.getTblPattern().getQualifiedDb());
- privileges = Lists.newArrayList(AccessPrivilege.READ_ONLY,
AccessPrivilege.ALL);
+ privileges = Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.READ_ONLY), new
AccessPrivilegeWithCols(AccessPrivilege.ALL));
stmt = new GrantStmt(new UserIdentity("testUser", "%"), null, new
TablePattern("testDb", "*"), privileges);
stmt.analyze(analyzer);
}
@@ -97,7 +98,7 @@ public class GrantStmtTest {
@Test
public void testResourceNormal() throws UserException {
String resourceName = "spark0";
- List<AccessPrivilege> privileges =
Lists.newArrayList(AccessPrivilege.USAGE_PRIV);
+ List<AccessPrivilegeWithCols> privileges = Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.USAGE_PRIV));
GrantStmt stmt = new GrantStmt(new UserIdentity("testUser", "%"),
null, new ResourcePattern(resourceName), privileges);
stmt.analyze(analyzer);
Assert.assertEquals(resourceName,
stmt.getResourcePattern().getResourceName());
@@ -113,7 +114,7 @@ public class GrantStmtTest {
public void testUserFail() throws AnalysisException, UserException {
GrantStmt stmt;
- List<AccessPrivilege> privileges =
Lists.newArrayList(AccessPrivilege.ALL);
+ List<AccessPrivilegeWithCols> privileges = Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.ALL));
stmt = new GrantStmt(new UserIdentity("", "%"), null, new
TablePattern("testDb", "*"), privileges);
stmt.analyze(analyzer);
Assert.fail("No exception throws.");
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/cooldown/CooldownConfHandlerTest.java
b/fe/fe-core/src/test/java/org/apache/doris/cooldown/CooldownConfHandlerTest.java
index ea3e239533..5b35321d91 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/cooldown/CooldownConfHandlerTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/cooldown/CooldownConfHandlerTest.java
@@ -23,6 +23,7 @@ import org.apache.doris.analysis.TablePattern;
import org.apache.doris.analysis.UserDesc;
import org.apache.doris.analysis.UserIdentity;
import org.apache.doris.catalog.AccessPrivilege;
+import org.apache.doris.catalog.AccessPrivilegeWithCols;
import org.apache.doris.catalog.Database;
import org.apache.doris.catalog.Env;
import org.apache.doris.catalog.MaterializedIndex;
@@ -69,7 +70,7 @@ public class CooldownConfHandlerTest extends
TestWithFeService {
user.analyze(SystemInfoService.DEFAULT_CLUSTER);
CreateUserStmt createUserStmt = new CreateUserStmt(new UserDesc(user));
Env.getCurrentEnv().getAuth().createUser(createUserStmt);
- List<AccessPrivilege> privileges =
Lists.newArrayList(AccessPrivilege.ADMIN_PRIV);
+ List<AccessPrivilegeWithCols> privileges = Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.ADMIN_PRIV));
TablePattern tablePattern = new TablePattern("*", "*", "*");
tablePattern.analyze(SystemInfoService.DEFAULT_CLUSTER);
GrantStmt grantStmt = new GrantStmt(user, null, tablePattern,
privileges);
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/mysql/privilege/AuthTest.java
b/fe/fe-core/src/test/java/org/apache/doris/mysql/privilege/AuthTest.java
index 417060de97..4978304177 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/mysql/privilege/AuthTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/mysql/privilege/AuthTest.java
@@ -30,6 +30,7 @@ import org.apache.doris.analysis.UserDesc;
import org.apache.doris.analysis.UserIdentity;
import org.apache.doris.analysis.WorkloadGroupPattern;
import org.apache.doris.catalog.AccessPrivilege;
+import org.apache.doris.catalog.AccessPrivilegeWithCols;
import org.apache.doris.catalog.DomainResolver;
import org.apache.doris.catalog.Env;
import org.apache.doris.common.AnalysisException;
@@ -44,6 +45,7 @@ import org.apache.doris.qe.QueryState;
import org.apache.doris.system.SystemInfoService;
import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
import mockit.Expectations;
import mockit.Mocked;
import org.junit.Assert;
@@ -373,7 +375,9 @@ public class AuthTest {
// 9. grant for cmy@'%'
TablePattern tablePattern = new TablePattern("*", "*");
- List<AccessPrivilege> privileges =
Lists.newArrayList(AccessPrivilege.CREATE_PRIV, AccessPrivilege.DROP_PRIV);
+ List<AccessPrivilegeWithCols> privileges = Lists
+ .newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.CREATE_PRIV),
+ new
AccessPrivilegeWithCols(AccessPrivilege.DROP_PRIV));
GrantStmt grantStmt = new GrantStmt(new UserIdentity("cmy", "%"),
null, tablePattern, privileges);
try {
@@ -409,7 +413,8 @@ public class AuthTest {
// 10. grant auth for non exist user
tablePattern = new TablePattern("*", "*");
- privileges = Lists.newArrayList(AccessPrivilege.CREATE_PRIV,
AccessPrivilege.DROP_PRIV);
+ privileges = Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.CREATE_PRIV),
+ new AccessPrivilegeWithCols(AccessPrivilege.DROP_PRIV));
grantStmt = new GrantStmt(new UserIdentity("nouser", "%"), null,
tablePattern, privileges);
try {
@@ -430,7 +435,8 @@ public class AuthTest {
// 11. grant auth for user with non exist host
tablePattern = new TablePattern("*", "*");
- privileges = Lists.newArrayList(AccessPrivilege.SELECT_PRIV,
AccessPrivilege.DROP_PRIV);
+ privileges = Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.SELECT_PRIV),
+ new AccessPrivilegeWithCols(AccessPrivilege.DROP_PRIV));
grantStmt = new GrantStmt(new UserIdentity("zhangsan", "%"), null,
tablePattern, privileges);
try {
@@ -451,7 +457,8 @@ public class AuthTest {
// 12. grant db auth to exist user
tablePattern = new TablePattern("db1", "*");
- privileges = Lists.newArrayList(AccessPrivilege.SELECT_PRIV,
AccessPrivilege.DROP_PRIV);
+ privileges = Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.SELECT_PRIV),
+ new AccessPrivilegeWithCols(AccessPrivilege.DROP_PRIV));
grantStmt = new GrantStmt(new UserIdentity("zhangsan", "192.%"), null,
tablePattern, privileges);
try {
@@ -482,7 +489,8 @@ public class AuthTest {
// 13. grant tbl auth to exist user
tablePattern = new TablePattern("db2", "tbl2");
- privileges = Lists.newArrayList(AccessPrivilege.ALTER_PRIV,
AccessPrivilege.DROP_PRIV);
+ privileges = Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.ALTER_PRIV),
+ new AccessPrivilegeWithCols(AccessPrivilege.DROP_PRIV));
grantStmt = new GrantStmt(new UserIdentity("zhangsan", "192.%"), null,
tablePattern, privileges);
try {
@@ -513,7 +521,7 @@ public class AuthTest {
// 13.1 grant external ctl tbl auth to exist user
tablePattern = new TablePattern("ext_ctl", "ext_db1", "ext_tbl1");
- privileges = Lists.newArrayList(AccessPrivilege.SELECT_PRIV);
+ privileges = Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.SELECT_PRIV));
grantStmt = new GrantStmt(new UserIdentity("zhangsan", "192.%"), null,
tablePattern, privileges);
try {
@@ -545,7 +553,8 @@ public class AuthTest {
// 14. grant db auth to zhangsan@['palo.domain1']
tablePattern = new TablePattern("db3", "*");
- privileges = Lists.newArrayList(AccessPrivilege.ALTER_PRIV,
AccessPrivilege.DROP_PRIV);
+ privileges = Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.ALTER_PRIV),
+ new AccessPrivilegeWithCols(AccessPrivilege.DROP_PRIV));
grantStmt = new GrantStmt(new UserIdentity("zhangsan", "palo.domain1",
true), null, tablePattern, privileges);
try {
@@ -570,7 +579,7 @@ public class AuthTest {
PrivPredicate.ALTER));
// 15. grant new auth to exist priv entry (exist ALTER/DROP, add
SELECT)
tablePattern = new TablePattern("db3", "*");
- privileges = Lists.newArrayList(AccessPrivilege.SELECT_PRIV);
+ privileges = Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.SELECT_PRIV));
grantStmt = new GrantStmt(new UserIdentity("zhangsan", "palo.domain1",
true), null, tablePattern, privileges);
try {
@@ -622,7 +631,7 @@ public class AuthTest {
// 16. revoke privs from non exist user
tablePattern = new TablePattern("*", "*");
- privileges = Lists.newArrayList(AccessPrivilege.SELECT_PRIV);
+ privileges = Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.SELECT_PRIV));
RevokeStmt revokeStmt = new RevokeStmt(new UserIdentity("nouser",
"%"), null, tablePattern, privileges);
try {
@@ -643,7 +652,7 @@ public class AuthTest {
// 17. revoke privs from non exist host
tablePattern = new TablePattern("*", "*");
- privileges = Lists.newArrayList(AccessPrivilege.SELECT_PRIV);
+ privileges = Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.SELECT_PRIV));
revokeStmt = new RevokeStmt(new UserIdentity("cmy", "172.%"), null,
tablePattern, privileges);
try {
@@ -664,7 +673,7 @@ public class AuthTest {
// 18. revoke privs from non exist db
tablePattern = new TablePattern("nodb", "*");
- privileges = Lists.newArrayList(AccessPrivilege.SELECT_PRIV);
+ privileges = Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.SELECT_PRIV));
revokeStmt = new RevokeStmt(new UserIdentity("cmy", "%"), null,
tablePattern, privileges);
try {
@@ -685,7 +694,7 @@ public class AuthTest {
// 19. revoke privs from user @ ip
tablePattern = new TablePattern("*", "*");
- privileges = Lists.newArrayList(AccessPrivilege.CREATE_PRIV);
+ privileges = Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.CREATE_PRIV));
revokeStmt = new RevokeStmt(new UserIdentity("cmy", "%"), null,
tablePattern, privileges);
try {
@@ -713,7 +722,7 @@ public class AuthTest {
// 19. revoke tbl privs from user @ ip
tablePattern = new TablePattern("db2", "tbl2");
- privileges = Lists.newArrayList(AccessPrivilege.ALTER_PRIV);
+ privileges = Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.ALTER_PRIV));
revokeStmt = new RevokeStmt(new UserIdentity("zhangsan", "192.%"),
null, tablePattern, privileges);
try {
@@ -749,7 +758,7 @@ public class AuthTest {
// 20. revoke privs from non exist user @ domain
tablePattern = new TablePattern("db2", "tbl2");
- privileges = Lists.newArrayList(AccessPrivilege.ALTER_PRIV);
+ privileges = Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.ALTER_PRIV));
revokeStmt = new RevokeStmt(new UserIdentity("zhangsan", "nodomain",
true), null, tablePattern, privileges);
try {
@@ -770,7 +779,7 @@ public class AuthTest {
// 21. revoke privs from non exist db from user @ domain
tablePattern = new TablePattern("nodb", "*");
- privileges = Lists.newArrayList(AccessPrivilege.ALTER_PRIV);
+ privileges = Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.ALTER_PRIV));
revokeStmt = new RevokeStmt(new UserIdentity("zhangsan",
"palo.domain1", true), null, tablePattern, privileges);
try {
@@ -791,7 +800,7 @@ public class AuthTest {
// 22. revoke privs from exist user @ domain
tablePattern = new TablePattern("db3", "*");
- privileges = Lists.newArrayList(AccessPrivilege.DROP_PRIV);
+ privileges = Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.DROP_PRIV));
revokeStmt = new RevokeStmt(new UserIdentity("zhangsan",
"palo.domain1", true), null, tablePattern, privileges);
try {
@@ -920,7 +929,8 @@ public class AuthTest {
Assert.assertTrue(hasException);
// 25. grant auth to non exist role, will create this new role
- privileges = Lists.newArrayList(AccessPrivilege.DROP_PRIV,
AccessPrivilege.SELECT_PRIV);
+ privileges = Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.DROP_PRIV),
+ new AccessPrivilegeWithCols(AccessPrivilege.SELECT_PRIV));
grantStmt = new GrantStmt(null, "role2", new TablePattern("*", "*"),
privileges);
try {
grantStmt.analyze(analyzer);
@@ -937,7 +947,8 @@ public class AuthTest {
}
// 26. grant auth to role
- privileges = Lists.newArrayList(AccessPrivilege.DROP_PRIV,
AccessPrivilege.SELECT_PRIV);
+ privileges = Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.DROP_PRIV),
+ new AccessPrivilegeWithCols(AccessPrivilege.SELECT_PRIV));
grantStmt = new GrantStmt(null, "role1", new TablePattern("*", "*"),
privileges);
try {
grantStmt.analyze(analyzer);
@@ -1008,7 +1019,7 @@ public class AuthTest {
PrivPredicate.DROP));
// 29. revoke auth on non exist db from role1
- privileges = Lists.newArrayList(AccessPrivilege.DROP_PRIV);
+ privileges = Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.DROP_PRIV));
revokeStmt = new RevokeStmt(null, "role1", new TablePattern("nodb",
"*"), privileges);
try {
revokeStmt.analyze(analyzer);
@@ -1027,7 +1038,7 @@ public class AuthTest {
Assert.assertTrue(hasException);
// 30. revoke auth from role1
- privileges = Lists.newArrayList(AccessPrivilege.DROP_PRIV);
+ privileges = Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.DROP_PRIV));
revokeStmt = new RevokeStmt(null, "role1", new TablePattern("*", "*"),
privileges);
try {
revokeStmt.analyze(analyzer);
@@ -1337,7 +1348,7 @@ public class AuthTest {
createUserStmt.analyze(analyzer);
auth.createUser(createUserStmt);
- privileges = Lists.newArrayList(AccessPrivilege.NODE_PRIV);
+ privileges = Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.NODE_PRIV));
// 40.1 grant to non-global level, which is not allowed
grantStmt = new GrantStmt(opUser, null, new TablePattern("db1", "*"),
privileges);
try {
@@ -1425,7 +1436,7 @@ public class AuthTest {
e.printStackTrace();
}
// Now, we grant grant_priv to opUser, and check if it can than grant
node_priv to other user
- privileges = Lists.newArrayList(AccessPrivilege.GRANT_PRIV);
+ privileges = Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.GRANT_PRIV));
grantStmt = new GrantStmt(opUser, null, new TablePattern("*", "*"),
privileges);
try {
new Expectations() {
@@ -1459,6 +1470,109 @@ public class AuthTest {
}
}
+ @Test
+ public void testColAuth() {
+ // create user
+ UserIdentity userIdentity = new UserIdentity("colUser", "%");
+ UserDesc userDesc = new UserDesc(userIdentity, "12345", true);
+ CreateUserStmt createUserStmt = new CreateUserStmt(false, userDesc,
null);
+ try {
+ createUserStmt.analyze(analyzer);
+ auth.createUser(createUserStmt);
+ } catch (UserException e) {
+ e.printStackTrace();
+ Assert.fail();
+ }
+
+ // test table is *
+ GrantStmt grantStmt = new GrantStmt(userIdentity, null, new
TablePattern("db1", "*"), Lists.newArrayList(
+ new AccessPrivilegeWithCols(AccessPrivilege.SELECT_PRIV,
Lists.newArrayList("a", "b"))));
+ try {
+ grantStmt.analyze(analyzer);
+ Assert.fail();
+ } catch (UserException e) {
+ e.printStackTrace();
+ }
+
+ // test CREATE_PRIV with col
+ grantStmt = new GrantStmt(userIdentity, null, new TablePattern("db1",
"tbl1"), Lists.newArrayList(
+ new AccessPrivilegeWithCols(AccessPrivilege.CREATE_PRIV,
Lists.newArrayList("a", "b"))));
+ try {
+ grantStmt.analyze(analyzer);
+ Assert.fail();
+ } catch (UserException e) {
+ e.printStackTrace();
+ }
+
+ // test Select_PRIV with col
+ grantStmt = new GrantStmt(userIdentity, null, new TablePattern("db1",
"tbl1"), Lists.newArrayList(
+ new AccessPrivilegeWithCols(AccessPrivilege.SELECT_PRIV,
Lists.newArrayList("a", "b"))));
+ try {
+ grantStmt.analyze(analyzer);
+ auth.grant(grantStmt);
+ } catch (UserException e) {
+ e.printStackTrace();
+ Assert.fail();
+ }
+ // check has select priv of column 'a'
+ try {
+ accessManager
+ .checkColumnsPriv(userIdentity,
SystemInfoService.DEFAULT_CLUSTER + ":db1", "tbl1",
+ Sets.newHashSet("a"), PrivPredicate.SELECT);
+ } catch (UserException e) {
+ e.printStackTrace();
+ Assert.fail();
+ }
+ // check has select priv of column 'c'
+ try {
+ accessManager
+ .checkColumnsPriv(userIdentity,
SystemInfoService.DEFAULT_CLUSTER + ":db1", "tbl1",
+ Sets.newHashSet("c"), PrivPredicate.SELECT);
+ Assert.fail();
+ } catch (UserException e) {
+ e.printStackTrace();
+ }
+ // check has load priv of column 'a'
+ try {
+ accessManager
+ .checkColumnsPriv(userIdentity,
SystemInfoService.DEFAULT_CLUSTER + ":db1", "tbl1",
+ Sets.newHashSet("a"), PrivPredicate.LOAD);
+ Assert.fail();
+ } catch (UserException e) {
+ e.printStackTrace();
+ }
+ // check 'create_priv' use checkColumnsPriv
+ try {
+ accessManager
+ .checkColumnsPriv(userIdentity,
SystemInfoService.DEFAULT_CLUSTER + ":db1", "tbl1",
+ Sets.newHashSet("a"), PrivPredicate.CREATE);
+ Assert.fail();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ // check show priv on ctl when has col priv
+ boolean hasPriv = accessManager.checkCtlPriv(userIdentity,
Auth.DEFAULT_CATALOG, PrivPredicate.SHOW);
+ if (!hasPriv) {
+ Assert.fail();
+ }
+ // check show priv on db when has col priv
+ hasPriv = accessManager.checkDbPriv(userIdentity,
Auth.DEFAULT_CATALOG, "db1", PrivPredicate.SHOW);
+ if (!hasPriv) {
+ Assert.fail();
+ }
+ // check show priv on tbl when has col priv
+ hasPriv = accessManager.checkTblPriv(userIdentity,
Auth.DEFAULT_CATALOG, "db1", "tbl1", PrivPredicate.SHOW);
+ if (!hasPriv) {
+ Assert.fail();
+ }
+ // check select priv on tbl when has col priv
+ hasPriv = accessManager.checkTblPriv(userIdentity,
Auth.DEFAULT_CATALOG, "db1", "tbl1", PrivPredicate.SELECT);
+ if (hasPriv) {
+ Assert.fail();
+ }
+ }
+
@Test
public void testGrantRole() {
UserIdentity userIdentity = new UserIdentity("testUser", "%");
@@ -1475,7 +1589,7 @@ public class AuthTest {
}
// grant select_priv on db 'db1' to role 'role1'
GrantStmt grantStmt = new GrantStmt(null, role, new
TablePattern("db1", "*"),
- Lists.newArrayList(AccessPrivilege.SELECT_PRIV));
+ Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.SELECT_PRIV)));
try {
grantStmt.analyze(analyzer);
auth.grant(grantStmt);
@@ -1544,7 +1658,8 @@ public class AuthTest {
ResourcePattern resourcePattern = new ResourcePattern(resourceName);
String anyResource = "*";
ResourcePattern anyResourcePattern = new ResourcePattern(anyResource);
- List<AccessPrivilege> usagePrivileges =
Lists.newArrayList(AccessPrivilege.USAGE_PRIV);
+ List<AccessPrivilegeWithCols> usagePrivileges = Lists
+ .newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.USAGE_PRIV));
UserDesc userDesc = new UserDesc(userIdentity, "12345", true);
// ------ grant|revoke resource to|from user ------
@@ -1583,8 +1698,9 @@ public class AuthTest {
Assert.assertFalse(accessManager.checkGlobalPriv(userIdentity,
PrivPredicate.USAGE));
// 3.1 grant 'notBelongToResourcePrivileges' on resource 'spark0' to
'testUser'@'%'
for (int i = 0; i < Privilege.notBelongToResourcePrivileges.length;
i++) {
- List<AccessPrivilege> notAllowedPrivileges = Lists
-
.newArrayList(AccessPrivilege.fromName(Privilege.notBelongToResourcePrivileges[i].getName()));
+ List<AccessPrivilegeWithCols> notAllowedPrivileges = Lists
+ .newArrayList(new AccessPrivilegeWithCols(
+
AccessPrivilege.fromName(Privilege.notBelongToResourcePrivileges[i].getName())));
grantStmt = new GrantStmt(userIdentity, null, resourcePattern,
notAllowedPrivileges);
try {
grantStmt.analyze(analyzer);
@@ -1791,7 +1907,8 @@ public class AuthTest {
}
// 1. grant db table priv to resource
- List<AccessPrivilege> privileges =
Lists.newArrayList(AccessPrivilege.SELECT_PRIV);
+ List<AccessPrivilegeWithCols> privileges = Lists
+ .newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.SELECT_PRIV));
grantStmt = new GrantStmt(userIdentity, null, resourcePattern,
privileges);
hasException = false;
try {
@@ -1824,7 +1941,8 @@ public class AuthTest {
WorkloadGroupPattern workloadGroupPattern = new
WorkloadGroupPattern(workloadGroupName);
String anyWorkloadGroup = "%";
WorkloadGroupPattern anyWorkloadGroupPattern = new
WorkloadGroupPattern(anyWorkloadGroup);
- List<AccessPrivilege> usagePrivileges =
Lists.newArrayList(AccessPrivilege.USAGE_PRIV);
+ List<AccessPrivilegeWithCols> usagePrivileges = Lists
+ .newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.USAGE_PRIV));
UserDesc userDesc = new UserDesc(userIdentity, "12345", true);
// ------ grant|revoke workload group to|from user ------
@@ -1863,8 +1981,9 @@ public class AuthTest {
Assert.assertFalse(accessManager.checkGlobalPriv(userIdentity,
PrivPredicate.USAGE));
// 3.1 grant 'notBelongToWorkloadGroupPrivileges'
for (int i = 0; i <
Privilege.notBelongToWorkloadGroupPrivileges.length; i++) {
- List<AccessPrivilege> notAllowedPrivileges = Lists.newArrayList(
-
AccessPrivilege.fromName(Privilege.notBelongToWorkloadGroupPrivileges[i].getName()));
+ List<AccessPrivilegeWithCols> notAllowedPrivileges =
Lists.newArrayList(
+ new AccessPrivilegeWithCols(
+
AccessPrivilege.fromName(Privilege.notBelongToWorkloadGroupPrivileges[i].getName())));
grantStmt = new GrantStmt(userIdentity, null,
workloadGroupPattern, notAllowedPrivileges);
try {
grantStmt.analyze(analyzer);
@@ -2072,7 +2191,8 @@ public class AuthTest {
}
// 1. grant db table priv to workload group
- List<AccessPrivilege> privileges =
Lists.newArrayList(AccessPrivilege.SELECT_PRIV);
+ List<AccessPrivilegeWithCols> privileges = Lists
+ .newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.SELECT_PRIV));
grantStmt = new GrantStmt(userIdentity, null, workloadGroupPattern,
privileges);
hasException = false;
try {
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/CheckRowPolicyTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/CheckRowPolicyTest.java
index 47e672db99..1464de74d8 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/CheckRowPolicyTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/CheckRowPolicyTest.java
@@ -17,12 +17,14 @@
package org.apache.doris.nereids.rules.analysis;
+import org.apache.doris.analysis.Analyzer;
import org.apache.doris.analysis.CreateUserStmt;
import org.apache.doris.analysis.GrantStmt;
import org.apache.doris.analysis.TablePattern;
import org.apache.doris.analysis.UserDesc;
import org.apache.doris.analysis.UserIdentity;
import org.apache.doris.catalog.AccessPrivilege;
+import org.apache.doris.catalog.AccessPrivilegeWithCols;
import org.apache.doris.catalog.AggregateType;
import org.apache.doris.catalog.Column;
import org.apache.doris.catalog.Database;
@@ -89,10 +91,12 @@ public class CheckRowPolicyTest extends TestWithFeService {
user.analyze(SystemInfoService.DEFAULT_CLUSTER);
CreateUserStmt createUserStmt = new CreateUserStmt(new UserDesc(user));
Env.getCurrentEnv().getAuth().createUser(createUserStmt);
- List<AccessPrivilege> privileges =
Lists.newArrayList(AccessPrivilege.ADMIN_PRIV);
+ List<AccessPrivilegeWithCols> privileges = Lists.newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.ADMIN_PRIV));
TablePattern tablePattern = new TablePattern("*", "*", "*");
tablePattern.analyze(SystemInfoService.DEFAULT_CLUSTER);
GrantStmt grantStmt = new GrantStmt(user, null, tablePattern,
privileges);
+ Analyzer analyzer = new Analyzer(connectContext.getEnv(),
connectContext);
+ grantStmt.analyze(analyzer);
Env.getCurrentEnv().getAuth().grant(grantStmt);
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/persist/PrivInfoTest.java
b/fe/fe-core/src/test/java/org/apache/doris/persist/PrivInfoTest.java
index d45c1e2ad7..5e02244afa 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/persist/PrivInfoTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/persist/PrivInfoTest.java
@@ -74,7 +74,7 @@ public class PrivInfoTest {
@Test
public void testWithTablePattern() throws IOException {
PrivInfo privInfo = new PrivInfo(UserIdentity.ROOT, TablePattern.ALL,
PrivBitSet.of(Privilege.DROP_PRIV),
- new byte[] {'a', 'b', 'c'}, "role");
+ new byte[] {'a', 'b', 'c'}, "role", null);
// 1. Write objects to file
File file = new File("./privInfo");
diff --git a/fe/fe-core/src/test/java/org/apache/doris/policy/PolicyTest.java
b/fe/fe-core/src/test/java/org/apache/doris/policy/PolicyTest.java
index 2415e0fb3c..b678f33614 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/policy/PolicyTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/policy/PolicyTest.java
@@ -17,6 +17,7 @@
package org.apache.doris.policy;
+import org.apache.doris.analysis.Analyzer;
import org.apache.doris.analysis.CreateUserStmt;
import org.apache.doris.analysis.Expr;
import org.apache.doris.analysis.GrantStmt;
@@ -25,6 +26,7 @@ import org.apache.doris.analysis.TablePattern;
import org.apache.doris.analysis.UserDesc;
import org.apache.doris.analysis.UserIdentity;
import org.apache.doris.catalog.AccessPrivilege;
+import org.apache.doris.catalog.AccessPrivilegeWithCols;
import org.apache.doris.catalog.Env;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.common.Config;
@@ -67,10 +69,13 @@ public class PolicyTest extends TestWithFeService {
user.analyze(SystemInfoService.DEFAULT_CLUSTER);
CreateUserStmt createUserStmt = new CreateUserStmt(new UserDesc(user));
Env.getCurrentEnv().getAuth().createUser(createUserStmt);
- List<AccessPrivilege> privileges =
Lists.newArrayList(AccessPrivilege.ADMIN_PRIV);
+ List<AccessPrivilegeWithCols> privileges = Lists
+ .newArrayList(new
AccessPrivilegeWithCols(AccessPrivilege.ADMIN_PRIV));
TablePattern tablePattern = new TablePattern("*", "*", "*");
tablePattern.analyze(SystemInfoService.DEFAULT_CLUSTER);
GrantStmt grantStmt = new GrantStmt(user, null, tablePattern,
privileges);
+ Analyzer analyzer = new Analyzer(connectContext.getEnv(),
connectContext);
+ grantStmt.analyze(analyzer);
Env.getCurrentEnv().getAuth().grant(grantStmt);
useUser("test_policy");
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]