Skip to content

Commit 03a5792

Browse files
committed
[fix](auth)Http api check auth (apache#40688)
- add auth for `api/meta/namespaces/internal/databases` - add auth for `api/show_table_data`
1 parent 55e92da commit 03a5792

File tree

9 files changed

+395
-14
lines changed

9 files changed

+395
-14
lines changed

fe/fe-common/src/main/java/org/apache/doris/common/Config.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ public class Config extends ConfigBase {
321321
+ "The connection is abandoned if the clock skew is larger than this value."})
322322
public static long max_bdbje_clock_delta_ms = 5000; // 5s
323323

324-
@ConfField(description = {"是否启用所有 http 接口的认证",
324+
@ConfField(mutable = true, description = {"是否启用所有 http 接口的认证",
325325
"Whether to enable all http interface authentication"}, varType = VariableAnnotation.EXPERIMENTAL)
326326
public static boolean enable_all_http_auth = false;
327327

fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/MetaInfoAction.java

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
* And meta info like databases, tables and schema
6060
*/
6161
@RestController
62+
@Deprecated
6263
public class MetaInfoAction extends RestBaseController {
6364

6465
private static final String NAMESPACES = "namespaces";

fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/ShowAction.java

+22-2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.apache.doris.common.proc.ProcNodeInterface;
3131
import org.apache.doris.common.proc.ProcResult;
3232
import org.apache.doris.common.proc.ProcService;
33+
import org.apache.doris.datasource.InternalCatalog;
3334
import org.apache.doris.ha.HAProtocol;
3435
import org.apache.doris.httpv2.entity.ResponseEntityBuilder;
3536
import org.apache.doris.mysql.privilege.PrivPredicate;
@@ -214,16 +215,23 @@ public Object show_data(HttpServletRequest request, HttpServletResponse response
214215
public Object show_table_data(HttpServletRequest request, HttpServletResponse response) {
215216
if (Config.enable_all_http_auth) {
216217
executeCheckPassword(request, response);
217-
checkGlobalAuth(ConnectContext.get().getCurrentUserIdentity(), PrivPredicate.ADMIN);
218218
}
219-
220219
String dbName = request.getParameter(DB_KEY);
221220
String tableName = request.getParameter(TABLE_KEY);
221+
222+
if (StringUtils.isEmpty(dbName) && StringUtils.isEmpty(tableName)) {
223+
return ResponseEntityBuilder.okWithCommonError("db and table cannot be empty at the same time");
224+
}
225+
222226
String singleReplica = request.getParameter(SINGLE_REPLICA_KEY);
223227
boolean singleReplicaBool = Boolean.parseBoolean(singleReplica);
224228
Map<String, Map<String, Long>> oneEntry = Maps.newHashMap();
225229
if (dbName != null) {
226230
String fullDbName = getFullDbName(dbName);
231+
if (!StringUtils.isEmpty(tableName) && Config.enable_all_http_auth) {
232+
checkTblAuth(ConnectContext.get().getCurrentUserIdentity(), fullDbName, tableName, PrivPredicate.SHOW);
233+
}
234+
227235
DatabaseIf db = Env.getCurrentInternalCatalog().getDbNullable(fullDbName);
228236
if (db == null) {
229237
return ResponseEntityBuilder.okWithCommonError("database " + fullDbName + " not found.");
@@ -236,6 +244,12 @@ public Object show_table_data(HttpServletRequest request, HttpServletResponse re
236244
if (db == null || !(db instanceof Database) || ((Database) db) instanceof MysqlCompatibleDatabase) {
237245
continue;
238246
}
247+
if (Config.enable_all_http_auth && !Env.getCurrentEnv().getAccessManager()
248+
.checkTblPriv(ConnectContext.get().getCurrentUserIdentity(),
249+
InternalCatalog.INTERNAL_CATALOG_NAME, db.getFullName(), tableName,
250+
PrivPredicate.SHOW)) {
251+
continue;
252+
}
239253
Map<String, Long> tablesEntry = getDataSizeOfTables(db, tableName, singleReplicaBool);
240254
oneEntry.put(ClusterNamespace.getNameFromFullName(db.getFullName()), tablesEntry);
241255
}
@@ -331,6 +345,12 @@ private Map<String, Long> getDataSizeOfTables(DatabaseIf db, String tableName, b
331345
if (Strings.isNullOrEmpty(tableName)) {
332346
List<Table> tables = db.getTables();
333347
for (Table table : tables) {
348+
if (Config.enable_all_http_auth && !Env.getCurrentEnv().getAccessManager()
349+
.checkTblPriv(ConnectContext.get(), InternalCatalog.INTERNAL_CATALOG_NAME, db.getFullName(),
350+
table.getName(),
351+
PrivPredicate.SHOW)) {
352+
continue;
353+
}
334354
Map<String, Long> tableEntry = getDataSizeOfTable(table, singleReplica);
335355
oneEntry.putAll(tableEntry);
336356
}

fe/fe-core/src/main/java/org/apache/doris/httpv2/restv2/MetaInfoActionV2.java

+14-11
Original file line numberDiff line numberDiff line change
@@ -86,28 +86,29 @@ public Object getAllDatabases(
8686
HttpServletRequest request, HttpServletResponse response) {
8787
checkWithCookie(request, response, false);
8888

89-
if (!ns.equalsIgnoreCase(SystemInfoService.DEFAULT_CLUSTER)) {
90-
return ResponseEntityBuilder.badRequest("Only support 'default_cluster' now");
89+
if (!ns.equalsIgnoreCase(SystemInfoService.DEFAULT_CLUSTER) && !ns.equalsIgnoreCase(
90+
InternalCatalog.INTERNAL_CATALOG_NAME)) {
91+
return ResponseEntityBuilder.badRequest("Only support 'default_cluster/internal' now");
9192
}
9293

9394
// 1. get all database with privilege
9495
List<String> dbNames = Env.getCurrentInternalCatalog().getDbNames();
95-
List<String> dbNameSet = Lists.newArrayList();
96+
List<String> filteredDbNames = Lists.newArrayList();
9697
for (String fullName : dbNames) {
9798
final String db = ClusterNamespace.getNameFromFullName(fullName);
9899
if (!Env.getCurrentEnv().getAccessManager()
99100
.checkDbPriv(ConnectContext.get(), InternalCatalog.INTERNAL_CATALOG_NAME, fullName,
100101
PrivPredicate.SHOW)) {
101102
continue;
102103
}
103-
dbNameSet.add(db);
104+
filteredDbNames.add(db);
104105
}
105106

106-
Collections.sort(dbNames);
107+
Collections.sort(filteredDbNames);
107108

108109
// handle limit offset
109-
Pair<Integer, Integer> fromToIndex = getFromToIndex(request, dbNames.size());
110-
return ResponseEntityBuilder.ok(dbNames.subList(fromToIndex.first, fromToIndex.second));
110+
Pair<Integer, Integer> fromToIndex = getFromToIndex(request, filteredDbNames.size());
111+
return ResponseEntityBuilder.ok(filteredDbNames.subList(fromToIndex.first, fromToIndex.second));
111112
}
112113

113114
/** Get all tables of a database
@@ -129,8 +130,9 @@ public Object getTables(
129130
HttpServletRequest request, HttpServletResponse response) {
130131
checkWithCookie(request, response, false);
131132

132-
if (!ns.equalsIgnoreCase(SystemInfoService.DEFAULT_CLUSTER)) {
133-
return ResponseEntityBuilder.badRequest("Only support 'default_cluster' now");
133+
if (!ns.equalsIgnoreCase(SystemInfoService.DEFAULT_CLUSTER) && !ns.equalsIgnoreCase(
134+
InternalCatalog.INTERNAL_CATALOG_NAME)) {
135+
return ResponseEntityBuilder.badRequest("Only support 'default_cluster/internal' now");
134136
}
135137

136138
String fullDbName = getFullDbName(dbName);
@@ -199,8 +201,9 @@ public Object getTableSchema(
199201
HttpServletRequest request, HttpServletResponse response) throws UserException {
200202
checkWithCookie(request, response, false);
201203

202-
if (!ns.equalsIgnoreCase(SystemInfoService.DEFAULT_CLUSTER)) {
203-
return ResponseEntityBuilder.badRequest("Only support 'default_cluster' now");
204+
if (!ns.equalsIgnoreCase(SystemInfoService.DEFAULT_CLUSTER) && !ns.equalsIgnoreCase(
205+
InternalCatalog.INTERNAL_CATALOG_NAME)) {
206+
return ResponseEntityBuilder.badRequest("Only support 'default_cluster/internal' now");
204207
}
205208

206209
String fullDbName = getFullDbName(dbName);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
import org.junit.Assert;
19+
20+
suite("test_http_meta_databases_auth","p0,auth,nonConcurrent") {
21+
String suiteName = "test_http_meta_databases_auth"
22+
String dbName = context.config.getDbNameByFile(context.file)
23+
String tableName = "${suiteName}_table"
24+
String user = "${suiteName}_user"
25+
String pwd = 'C123_567p'
26+
try_sql("DROP USER ${user}")
27+
sql """CREATE USER '${user}' IDENTIFIED BY '${pwd}'"""
28+
try {
29+
sql """ ADMIN SET ALL FRONTENDS CONFIG ("enable_all_http_auth" = "true"); """
30+
def getDatabases = { check_func ->
31+
httpTest {
32+
basicAuthorization "${user}","${pwd}"
33+
endpoint "${context.config.feHttpAddress}"
34+
uri "/rest/v2/api/meta/namespaces/default_cluster/databases"
35+
op "get"
36+
check check_func
37+
}
38+
}
39+
40+
getDatabases.call() {
41+
respCode, body ->
42+
log.info("body:${body}")
43+
assertFalse("${body}".contains("${dbName}"))
44+
}
45+
46+
sql """grant select_priv on ${dbName} to ${user}"""
47+
48+
getDatabases.call() {
49+
respCode, body ->
50+
log.info("body:${body}")
51+
assertTrue("${body}".contains("${dbName}"))
52+
}
53+
54+
try_sql("DROP USER ${user}")
55+
} finally {
56+
sql """ ADMIN SET ALL FRONTENDS CONFIG ("enable_all_http_auth" = "false"); """
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
import org.junit.Assert;
19+
20+
suite("test_http_meta_tables_auth","p0,auth,nonConcurrent") {
21+
String suiteName = "test_http_meta_tables_auth"
22+
String dbName = context.config.getDbNameByFile(context.file)
23+
String tableName = "${suiteName}_table"
24+
String user = "${suiteName}_user"
25+
String pwd = 'C123_567p'
26+
try_sql("DROP USER ${user}")
27+
sql """CREATE USER '${user}' IDENTIFIED BY '${pwd}'"""
28+
sql """drop table if exists `${tableName}`"""
29+
sql """
30+
CREATE TABLE `${tableName}` (
31+
`k1` int,
32+
`k2` int
33+
) ENGINE=OLAP
34+
DISTRIBUTED BY random BUCKETS auto
35+
PROPERTIES ('replication_num' = '1') ;
36+
"""
37+
try {
38+
sql """ ADMIN SET ALL FRONTENDS CONFIG ("enable_all_http_auth" = "true"); """
39+
def getTables = { check_func ->
40+
httpTest {
41+
basicAuthorization "${user}","${pwd}"
42+
endpoint "${context.config.feHttpAddress}"
43+
uri "/rest/v2/api/meta/namespaces/default_cluster/databases/${dbName}/tables"
44+
op "get"
45+
check check_func
46+
}
47+
}
48+
49+
getTables.call() {
50+
respCode, body ->
51+
log.info("body:${body}")
52+
assertFalse("${body}".contains("${tableName}"))
53+
}
54+
55+
sql """grant select_priv on ${dbName}.${tableName} to ${user}"""
56+
57+
getTables.call() {
58+
respCode, body ->
59+
log.info("body:${body}")
60+
assertTrue("${body}".contains("${tableName}"))
61+
}
62+
63+
sql """drop table if exists `${tableName}`"""
64+
try_sql("DROP USER ${user}")
65+
} finally {
66+
sql """ ADMIN SET ALL FRONTENDS CONFIG ("enable_all_http_auth" = "false"); """
67+
}
68+
69+
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
import org.junit.Assert;
19+
20+
suite("test_http_meta_tables_schema_auth","p0,auth,nonConcurrent") {
21+
String suiteName = "test_http_meta_tables_schema_auth"
22+
String dbName = context.config.getDbNameByFile(context.file)
23+
String tableName = "${suiteName}_table"
24+
String user = "${suiteName}_user"
25+
String pwd = 'C123_567p'
26+
try_sql("DROP USER ${user}")
27+
sql """CREATE USER '${user}' IDENTIFIED BY '${pwd}'"""
28+
sql """drop table if exists `${tableName}`"""
29+
sql """
30+
CREATE TABLE `${tableName}` (
31+
`k1` int,
32+
`k2` int
33+
) ENGINE=OLAP
34+
DISTRIBUTED BY random BUCKETS auto
35+
PROPERTIES ('replication_num' = '1') ;
36+
"""
37+
38+
try {
39+
sql """ ADMIN SET ALL FRONTENDS CONFIG ("enable_all_http_auth" = "true"); """
40+
def getSchema = { check_func ->
41+
httpTest {
42+
basicAuthorization "${user}","${pwd}"
43+
endpoint "${context.config.feHttpAddress}"
44+
uri "/rest/v2/api/meta/namespaces/default_cluster/databases/${dbName}/tables/${tableName}/schema"
45+
op "get"
46+
check check_func
47+
}
48+
}
49+
50+
getSchema.call() {
51+
respCode, body ->
52+
log.info("body:${body}")
53+
assertTrue("${body}".contains("401"))
54+
}
55+
56+
sql """grant select_priv on ${dbName}.${tableName} to ${user}"""
57+
58+
getSchema.call() {
59+
respCode, body ->
60+
log.info("body:${body}")
61+
assertTrue("${body}".contains("${tableName}"))
62+
}
63+
64+
sql """drop table if exists `${tableName}`"""
65+
try_sql("DROP USER ${user}")
66+
} finally {
67+
sql """ ADMIN SET ALL FRONTENDS CONFIG ("enable_all_http_auth" = "false"); """
68+
}
69+
}

0 commit comments

Comments
 (0)