Skip to content

Commit dba0e76

Browse files
committed
Add test cases to reproduce HiveAccessControlException
1 parent 52b6fc3 commit dba0e76

17 files changed

+705
-11
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.iceberg.hive;
21+
22+
import org.apache.hadoop.hive.conf.HiveConf;
23+
import org.apache.hadoop.hive.ql.security.HiveAuthenticationProvider;
24+
import org.apache.hadoop.hive.ql.security.authorization.plugin.HiveAuthorizer;
25+
import org.apache.hadoop.hive.ql.security.authorization.plugin.HiveAuthorizerFactory;
26+
import org.apache.hadoop.hive.ql.security.authorization.plugin.HiveAuthzSessionContext;
27+
import org.apache.hadoop.hive.ql.security.authorization.plugin.HiveMetastoreClientFactory;
28+
29+
public class HiveAuthorizerFactoryForTest implements HiveAuthorizerFactory {
30+
@Override
31+
public HiveAuthorizer createHiveAuthorizer(HiveMetastoreClientFactory metastoreClientFactory, HiveConf conf,
32+
HiveAuthenticationProvider hiveAuthenticator, HiveAuthzSessionContext ctx) {
33+
return new HiveAuthorizerForTest(hiveAuthenticator);
34+
}
35+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.iceberg.hive;
21+
22+
import java.util.List;
23+
import org.apache.hadoop.hive.conf.HiveConf;
24+
import org.apache.hadoop.hive.ql.security.HiveAuthenticationProvider;
25+
import org.apache.hadoop.hive.ql.security.authorization.plugin.AbstractHiveAuthorizer;
26+
import org.apache.hadoop.hive.ql.security.authorization.plugin.HiveAccessControlException;
27+
import org.apache.hadoop.hive.ql.security.authorization.plugin.HiveAuthzContext;
28+
import org.apache.hadoop.hive.ql.security.authorization.plugin.HiveOperationType;
29+
import org.apache.hadoop.hive.ql.security.authorization.plugin.HivePrincipal;
30+
import org.apache.hadoop.hive.ql.security.authorization.plugin.HivePrivilege;
31+
import org.apache.hadoop.hive.ql.security.authorization.plugin.HivePrivilegeInfo;
32+
import org.apache.hadoop.hive.ql.security.authorization.plugin.HivePrivilegeObject;
33+
import org.apache.hadoop.hive.ql.security.authorization.plugin.HiveRoleGrant;
34+
import org.slf4j.Logger;
35+
import org.slf4j.LoggerFactory;
36+
37+
public class HiveAuthorizerForTest extends AbstractHiveAuthorizer {
38+
public static final String PERMISSION_TEST_USER = "permission_test_user";
39+
private static final Logger LOG = LoggerFactory.getLogger(HiveAuthorizerForTest.class);
40+
41+
private final HiveAuthenticationProvider authenticator;
42+
43+
public HiveAuthorizerForTest(HiveAuthenticationProvider authenticator) {
44+
this.authenticator = authenticator;
45+
}
46+
47+
@Override
48+
public VERSION getVersion() {
49+
return null;
50+
}
51+
52+
@Override
53+
public void grantPrivileges(List<HivePrincipal> hivePrincipals, List<HivePrivilege> hivePrivileges,
54+
HivePrivilegeObject hivePrivObject, HivePrincipal grantorPrincipal, boolean grantOption) {
55+
}
56+
57+
@Override
58+
public void revokePrivileges(List<HivePrincipal> hivePrincipals, List<HivePrivilege> hivePrivileges,
59+
HivePrivilegeObject hivePrivObject, HivePrincipal grantorPrincipal, boolean grantOption) {
60+
}
61+
62+
@Override
63+
public void createRole(String roleName, HivePrincipal adminGrantor) {
64+
}
65+
66+
@Override
67+
public void dropRole(String roleName) {
68+
}
69+
70+
@Override
71+
public List<HiveRoleGrant> getPrincipalGrantInfoForRole(String roleName) {
72+
return List.of();
73+
}
74+
75+
@Override
76+
public List<HiveRoleGrant> getRoleGrantInfoForPrincipal(HivePrincipal principal) {
77+
return List.of();
78+
}
79+
80+
@Override
81+
public void grantRole(List<HivePrincipal> hivePrincipals, List<String> roles, boolean grantOption,
82+
HivePrincipal grantorPrinc) {
83+
}
84+
85+
@Override
86+
public void revokeRole(List<HivePrincipal> hivePrincipals, List<String> roles, boolean grantOption,
87+
HivePrincipal grantorPrinc) {
88+
89+
}
90+
91+
@Override
92+
public void checkPrivileges(HiveOperationType hiveOpType, List<HivePrivilegeObject> inputsHObjs,
93+
List<HivePrivilegeObject> outputHObjs, HiveAuthzContext context) throws HiveAccessControlException {
94+
LOG.info("Checking privileges. User={}, Operation={}, inputs={}, outputs={}", authenticator.getUserName(),
95+
hiveOpType, inputsHObjs, outputHObjs);
96+
if (PERMISSION_TEST_USER.equals(authenticator.getUserName())) {
97+
throw new HiveAccessControlException(String.format("Unauthorized. Operation=%s, inputs=%s, outputs=%s",
98+
hiveOpType, inputsHObjs, outputHObjs));
99+
}
100+
}
101+
102+
@Override
103+
public List<HivePrivilegeObject> filterListCmdObjects(List<HivePrivilegeObject> listObjs, HiveAuthzContext context) {
104+
return List.of();
105+
}
106+
107+
@Override
108+
public List<String> getAllRoles() {
109+
return List.of();
110+
}
111+
112+
@Override
113+
public List<HivePrivilegeInfo> showPrivileges(HivePrincipal principal, HivePrivilegeObject privObj) {
114+
return List.of();
115+
}
116+
117+
@Override
118+
public void setCurrentRole(String roleName) {
119+
120+
}
121+
122+
@Override
123+
public List<String> getCurrentRoleNames() {
124+
return List.of();
125+
}
126+
127+
@Override
128+
public void applyAuthorizationConfigPolicy(HiveConf hiveConf) {
129+
130+
}
131+
132+
@Override
133+
public List<HivePrivilegeObject> applyRowFilterAndColumnMasking(HiveAuthzContext context,
134+
List<HivePrivilegeObject> privObjs) {
135+
return List.of();
136+
}
137+
138+
@Override
139+
public boolean needTransform() {
140+
return false;
141+
}
142+
}

iceberg/iceberg-catalog/src/test/java/org/apache/iceberg/hive/HiveMetastoreExtension.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public void beforeAll(ExtensionContext extensionContext) throws Exception {
4949
}
5050
}
5151

52-
metastore.start(hiveConfWithOverrides, 5, true);
52+
metastore.start(hiveConfWithOverrides, 20, true);
5353
metastoreClient = new HiveMetaStoreClient(hiveConfWithOverrides);
5454
if (null != databaseName) {
5555
String dbPath = metastore.getDatabasePath(databaseName);
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.iceberg.hive;
21+
22+
import java.security.PrivilegedExceptionAction;
23+
import java.util.Collections;
24+
import java.util.List;
25+
import java.util.Map;
26+
import java.util.UUID;
27+
import java.util.function.Consumer;
28+
import org.apache.hadoop.hive.metastore.conf.MetastoreConf.ConfVars;
29+
import org.apache.hadoop.hive.ql.security.authorization.plugin.metastore.HiveMetaStoreAuthorizer;
30+
import org.apache.hadoop.security.UserGroupInformation;
31+
import org.apache.iceberg.CatalogProperties;
32+
import org.apache.iceberg.CatalogUtil;
33+
import org.apache.iceberg.Schema;
34+
import org.apache.iceberg.catalog.Namespace;
35+
import org.apache.iceberg.catalog.TableIdentifier;
36+
import org.apache.iceberg.exceptions.ForbiddenException;
37+
import org.assertj.core.api.Assertions;
38+
import org.junit.jupiter.api.AfterEach;
39+
import org.junit.jupiter.api.Test;
40+
import org.junit.jupiter.api.extension.RegisterExtension;
41+
42+
public class TestHiveCatalogAccessControl {
43+
@RegisterExtension
44+
private static final HiveMetastoreExtension HIVE_METASTORE_EXTENSION = HiveMetastoreExtension.builder()
45+
.withConfig(Map.of(
46+
ConfVars.HIVE_AUTHORIZATION_MANAGER.getVarname(), HiveAuthorizerFactoryForTest.class.getName(),
47+
ConfVars.PRE_EVENT_LISTENERS.getVarname(), HiveMetaStoreAuthorizer.class.getName()
48+
)).build();
49+
50+
@AfterEach
51+
public void afterEach() throws Exception {
52+
HIVE_METASTORE_EXTENSION.metastore().reset();
53+
}
54+
55+
@Test
56+
public void testNamespace() throws Exception {
57+
var namespace = Namespace.of("permission_test_db");
58+
asAuthorized(catalog -> {
59+
catalog.createNamespace(namespace, Collections.emptyMap());
60+
});
61+
asUnauthorized(catalog -> {
62+
// Should HMS omit namespaces?
63+
Assertions.assertThat(catalog.listNamespaces()).isEqualTo(List.of(Namespace.of("default"), namespace));
64+
Assertions.assertThatThrownBy(() ->
65+
catalog.namespaceExists(namespace)).isInstanceOf(ForbiddenException.class);
66+
Assertions.assertThatThrownBy(() ->
67+
catalog.loadNamespaceMetadata(namespace)).isInstanceOf(ForbiddenException.class);
68+
Assertions.assertThatThrownBy(() ->
69+
catalog.createNamespace(Namespace.of("new_db"))).isInstanceOf(ForbiddenException.class);
70+
Assertions.assertThatThrownBy(() ->
71+
catalog.dropNamespace(namespace)).isInstanceOf(ForbiddenException.class);
72+
Assertions.assertThatThrownBy(() ->
73+
catalog.setProperties(namespace, Collections.singletonMap("key", "value")))
74+
.isInstanceOf(ForbiddenException.class);
75+
Assertions.assertThatThrownBy(() ->
76+
catalog.removeProperties(namespace, Collections.singleton("key"))).isInstanceOf(ForbiddenException.class);
77+
});
78+
}
79+
80+
@Test
81+
public void testTable() throws Exception {
82+
Namespace namespace = Namespace.of("permission_test_db");
83+
TableIdentifier table = TableIdentifier.of(namespace, "permission_test_table");
84+
asAuthorized(catalog -> {
85+
catalog.createNamespace(namespace, Collections.emptyMap());
86+
catalog.createTable(table, new Schema());
87+
});
88+
asUnauthorized(catalog -> {
89+
// Should HMS omit namespaces?
90+
Assertions.assertThat(catalog.listTables(namespace)).isEqualTo(Collections.singletonList(table));
91+
Assertions.assertThatThrownBy(() ->
92+
catalog.tableExists(table)).isInstanceOf(ForbiddenException.class);
93+
Assertions.assertThatThrownBy(() ->
94+
catalog.loadTable(table)).isInstanceOf(ForbiddenException.class);
95+
Assertions.assertThatThrownBy(() ->
96+
catalog.createTable(TableIdentifier.of(namespace, "new_tbl"), new Schema()))
97+
.isInstanceOf(ForbiddenException.class);
98+
Assertions.assertThatThrownBy(() ->
99+
catalog.renameTable(table, TableIdentifier.of(namespace, "new_tbl"))).isInstanceOf(ForbiddenException.class);
100+
Assertions.assertThatThrownBy(() ->
101+
catalog.dropTable(table)).isInstanceOf(ForbiddenException.class);
102+
});
103+
}
104+
105+
@Test
106+
public void testView() throws Exception {
107+
Namespace namespace = Namespace.of("permission_test_db");
108+
TableIdentifier view = TableIdentifier.of(namespace, "permission_test_view");
109+
asAuthorized(catalog -> {
110+
catalog.createNamespace(namespace, Collections.emptyMap());
111+
catalog.buildView(view).withQuery("hive", "SELECT 1 AS id").withSchema(new Schema())
112+
.withDefaultNamespace(namespace).create();
113+
});
114+
asUnauthorized(catalog -> {
115+
// Should HMS omit namespaces?
116+
Assertions.assertThat(catalog.listViews(namespace)).isEqualTo(Collections.singletonList(view));
117+
Assertions.assertThatThrownBy(() ->
118+
catalog.viewExists(view)).isInstanceOf(ForbiddenException.class);
119+
Assertions.assertThatThrownBy(() ->
120+
catalog.loadView(view)).isInstanceOf(ForbiddenException.class);
121+
Assertions.assertThatThrownBy(() ->
122+
catalog.buildView(TableIdentifier.of(namespace, "new_view")).withQuery("hive", "SELECT 1 AS id")
123+
.withSchema(new Schema()).withDefaultNamespace(namespace).create())
124+
.isInstanceOf(ForbiddenException.class);
125+
Assertions.assertThatThrownBy(() ->
126+
catalog.renameView(view, TableIdentifier.of(namespace, "new_view"))).isInstanceOf(ForbiddenException.class);
127+
Assertions.assertThatThrownBy(() ->
128+
catalog.dropView(view)).isInstanceOf(ForbiddenException.class);
129+
});
130+
}
131+
132+
@Test
133+
public void testTransaction() throws Exception {
134+
var namespace = Namespace.of("permission_test_db");
135+
asAuthorized(catalog -> {
136+
catalog.createNamespace(namespace, Collections.emptyMap());
137+
});
138+
asUnauthorized(catalog -> {
139+
Assertions.assertThatThrownBy(() ->
140+
catalog.newCreateTableTransaction(TableIdentifier.of(namespace, "new_table"), new Schema()))
141+
.isInstanceOf(ForbiddenException.class);
142+
Assertions.assertThatThrownBy(() ->
143+
catalog.newReplaceTableTransaction(TableIdentifier.of(namespace, "new_table"), new Schema(), true))
144+
.isInstanceOf(ForbiddenException.class);
145+
});
146+
}
147+
148+
private static void asAuthorized(Consumer<HiveCatalog> consumer) throws Exception {
149+
withUser("authorized_user", consumer);
150+
}
151+
152+
private static void asUnauthorized(Consumer<HiveCatalog> consumer) throws Exception {
153+
withUser(HiveAuthorizerForTest.PERMISSION_TEST_USER, consumer);
154+
}
155+
156+
private static void withUser(String username, Consumer<HiveCatalog> consumer) throws Exception {
157+
var ugi = UserGroupInformation.createRemoteUser(username);
158+
ugi.doAs((PrivilegedExceptionAction<Void>) () -> {
159+
try (HiveCatalog catalog = createCatalog()) {
160+
consumer.accept(catalog);
161+
return null;
162+
}
163+
});
164+
}
165+
166+
private static HiveCatalog createCatalog() {
167+
return (HiveCatalog)
168+
CatalogUtil.loadCatalog(
169+
HiveCatalog.class.getName(),
170+
UUID.randomUUID().toString(),
171+
Map.of(
172+
CatalogProperties.CLIENT_POOL_CACHE_KEYS, "ugi"
173+
),
174+
HIVE_METASTORE_EXTENSION.hiveConf());
175+
}
176+
}

0 commit comments

Comments
 (0)