Skip to content

Commit b5bccec

Browse files
author
Dmitriy Fingerman
committed
HIVE-29020: Iceberg: Validate HMS REST Catalog Client with OAuth2
1 parent b115c85 commit b5bccec

File tree

11 files changed

+298
-82
lines changed

11 files changed

+298
-82
lines changed

itests/hive-iceberg/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@
4646
<classifier>tests</classifier>
4747
<version>${project.version}</version>
4848
</dependency>
49+
<dependency>
50+
<groupId>org.keycloak</groupId>
51+
<artifactId>keycloak-admin-client</artifactId>
52+
<version>${keycloak.version}</version>
53+
<scope>test</scope>
54+
</dependency>
4955
<dependency>
5056
<groupId>org.apache.hive</groupId>
5157
<artifactId>hive-standalone-metastore-common</artifactId>

itests/hive-iceberg/src/test/java/org/apache/hive/TestHiveRESTCatalogClientIT.java renamed to itests/hive-iceberg/src/test/java/org/apache/hive/TestHiveRESTCatalogClientITBase.java

Lines changed: 33 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import org.apache.hadoop.hive.metastore.HiveMetaHookLoader;
2424
import org.apache.hadoop.hive.metastore.HiveMetaStoreClient;
2525
import org.apache.hadoop.hive.metastore.IMetaStoreClient;
26-
import org.apache.hadoop.hive.metastore.ServletSecurity;
2726
import org.apache.hadoop.hive.metastore.api.Database;
2827
import org.apache.hadoop.hive.metastore.api.FieldSchema;
2928
import org.apache.hadoop.hive.metastore.api.MetaException;
@@ -46,18 +45,15 @@
4645
import org.apache.iceberg.hive.CatalogUtils;
4746
import org.apache.iceberg.hive.HiveSchemaUtil;
4847
import org.apache.iceberg.rest.extension.HiveRESTCatalogServerExtension;
49-
import org.junit.jupiter.api.AfterAll;
48+
import org.junit.jupiter.api.AfterEach;
5049
import org.junit.jupiter.api.Assertions;
51-
import org.junit.jupiter.api.BeforeAll;
50+
import org.junit.jupiter.api.BeforeEach;
5251
import org.junit.jupiter.api.Test;
53-
import org.junit.jupiter.api.TestInstance;
54-
import org.junit.jupiter.api.extension.RegisterExtension;
55-
56-
import java.util.Collections;
57-
import java.util.Map;
5852

5953
import java.util.Arrays;
54+
import java.util.Collections;
6055
import java.util.List;
56+
import java.util.Map;
6157

6258
import static org.junit.jupiter.api.Assertions.assertThrows;
6359

@@ -67,38 +63,36 @@
6763
* The flow is as follows:
6864
* Hive ql wrapper --> HiveMetaStoreClient --> HiveRESTCatalogClient --> HMS RESTCatalog Server --> HMS
6965
*/
70-
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
71-
public class TestHiveRESTCatalogClientIT {
72-
73-
private static final String DB_NAME = "ice_db";
74-
private static final String TABLE_NAME = "ice_tbl";
75-
private static final String CATALOG_NAME = "ice01";
76-
private static final String HIVE_ICEBERG_STORAGE_HANDLER = "org.apache.iceberg.mr.hive.HiveIcebergStorageHandler";
66+
public abstract class TestHiveRESTCatalogClientITBase {
67+
68+
static final String DB_NAME = "ice_db";
69+
static final String TABLE_NAME = "ice_tbl";
70+
static final String CATALOG_NAME = "ice01";
71+
static final String HIVE_ICEBERG_STORAGE_HANDLER = "org.apache.iceberg.mr.hive.HiveIcebergStorageHandler";
72+
static final String restCatalogPrefix = String.format("%s%s.", CatalogUtils.CATALOG_CONFIG_PREFIX, CATALOG_NAME);
73+
74+
HiveConf hiveConf;
75+
Configuration conf;
76+
Hive hive;
77+
IMetaStoreClient msClient;
7778

78-
private Configuration conf;
79-
private HiveConf hiveConf;
80-
private Hive hive;
81-
82-
private IMetaStoreClient msClient;
79+
abstract HiveRESTCatalogServerExtension getHiveRESTCatalogServerExtension();
8380

84-
@RegisterExtension
85-
private static final HiveRESTCatalogServerExtension REST_CATALOG_EXTENSION =
86-
HiveRESTCatalogServerExtension.builder(ServletSecurity.AuthType.NONE)
87-
.addMetaStoreSchemaClassName(ITestsSchemaInfo.class)
88-
.build();
81+
public void setupConf() {
82+
HiveRESTCatalogServerExtension restCatalogExtension = getHiveRESTCatalogServerExtension();
8983

90-
@BeforeAll
91-
public void setup() throws Exception {
92-
// Starting msClient with Iceberg REST Catalog client underneath
93-
String restCatalogPrefix = String.format("%s%s.", CatalogUtils.CATALOG_CONFIG_PREFIX, CATALOG_NAME);
94-
95-
conf = REST_CATALOG_EXTENSION.getConf();
84+
conf = restCatalogExtension.getConf();
9685

9786
MetastoreConf.setVar(conf, MetastoreConf.ConfVars.METASTORE_CLIENT_IMPL,
9887
"org.apache.iceberg.hive.client.HiveRESTCatalogClient");
9988
conf.set(MetastoreConf.ConfVars.CATALOG_DEFAULT.getVarname(), CATALOG_NAME);
100-
conf.set(restCatalogPrefix + "uri", REST_CATALOG_EXTENSION.getRestEndpoint());
89+
conf.set(restCatalogPrefix + "uri", restCatalogExtension.getRestEndpoint());
10190
conf.set(restCatalogPrefix + "type", CatalogUtil.ICEBERG_CATALOG_TYPE_REST);
91+
}
92+
93+
@BeforeEach
94+
void setup() throws Exception {
95+
setupConf();
10296

10397
HiveMetaHookLoader hookLoader = tbl -> {
10498
HiveStorageHandler storageHandler;
@@ -109,18 +103,19 @@ public void setup() throws Exception {
109103
}
110104
return storageHandler == null ? null : storageHandler.getMetaHook();
111105
};
112-
106+
113107
msClient = new HiveMetaStoreClient(conf, hookLoader);
114108
hiveConf = new HiveConf(conf, HiveConf.class);
115109
hive = Hive.get(hiveConf);
116110
}
117111

118-
@AfterAll public void tearDown() {
112+
@AfterEach
113+
public void tearDown() {
119114
if (msClient != null) {
120115
msClient.close();
121116
}
122117
}
123-
118+
124119
@Test
125120
public void testIceberg() throws Exception {
126121

@@ -142,7 +137,7 @@ public void testIceberg() throws Exception {
142137
// --- Get Databases ---
143138
List<String> dbs = msClient.getDatabases(CATALOG_NAME, "ice_*");
144139
Assertions.assertEquals(1, dbs.size());
145-
Assertions.assertEquals(DB_NAME, dbs.get(0));
140+
Assertions.assertEquals(DB_NAME, dbs.getFirst());
146141

147142
// --- Get All Databases ---
148143
List<String> allDbs = msClient.getAllDatabases(CATALOG_NAME);
@@ -151,7 +146,7 @@ public void testIceberg() throws Exception {
151146
Assertions.assertTrue(allDbs.contains(DB_NAME));
152147

153148
// --- Create Table ---
154-
org.apache.hadoop.hive.metastore.api.Table tTable = createPartitionedTable(msClient,
149+
Table tTable = createPartitionedTable(msClient,
155150
CATALOG_NAME, DB_NAME, TABLE_NAME, new java.util.HashMap<>());
156151
Assertions.assertNotNull(tTable);
157152
Assertions.assertEquals(HiveMetaHook.ICEBERG, tTable.getParameters().get(HiveMetaHook.TABLE_TYPE));
@@ -166,7 +161,7 @@ public void testIceberg() throws Exception {
166161
Assertions.assertTrue(msClient.tableExists(CATALOG_NAME, DB_NAME, TABLE_NAME));
167162

168163
// --- Get Table ---
169-
org.apache.hadoop.hive.metastore.api.Table table = msClient.getTable(CATALOG_NAME, DB_NAME, TABLE_NAME);
164+
Table table = msClient.getTable(CATALOG_NAME, DB_NAME, TABLE_NAME);
170165
Assertions.assertEquals(DB_NAME, table.getDbName());
171166
Assertions.assertEquals(TABLE_NAME, table.getTableName());
172167
Assertions.assertEquals(HIVE_ICEBERG_STORAGE_HANDLER, table.getParameters().get("storage_handler"));
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.hive;
19+
20+
import org.apache.hadoop.hive.metastore.ServletSecurity;
21+
import org.apache.iceberg.rest.extension.HiveRESTCatalogServerExtension;
22+
import org.junit.jupiter.api.TestInstance;
23+
import org.junit.jupiter.api.extension.RegisterExtension;
24+
25+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
26+
public class TestHiveRESTCatalogClientITNoAuth extends TestHiveRESTCatalogClientITBase {
27+
28+
@RegisterExtension
29+
private static final HiveRESTCatalogServerExtension REST_CATALOG_EXTENSION =
30+
HiveRESTCatalogServerExtension.builder(ServletSecurity.AuthType.NONE)
31+
.addMetaStoreSchemaClassName(ITestsSchemaInfo.class)
32+
.build();
33+
34+
@Override
35+
HiveRESTCatalogServerExtension getHiveRESTCatalogServerExtension() {
36+
return REST_CATALOG_EXTENSION;
37+
}
38+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.hive;
19+
20+
import org.apache.hadoop.hive.metastore.ServletSecurity;
21+
import org.apache.iceberg.rest.extension.HiveRESTCatalogServerExtension;
22+
import org.junit.jupiter.api.TestInstance;
23+
import org.junit.jupiter.api.extension.RegisterExtension;
24+
25+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
26+
public class TestHiveRESTCatalogClientITOauth2 extends TestHiveRESTCatalogClientITBase {
27+
28+
@RegisterExtension
29+
private static final HiveRESTCatalogServerExtension REST_CATALOG_EXTENSION =
30+
HiveRESTCatalogServerExtension.builder(ServletSecurity.AuthType.OAUTH2)
31+
.addMetaStoreSchemaClassName(ITestsSchemaInfo.class)
32+
.build();
33+
34+
public void setupConf() {
35+
super.setupConf();
36+
37+
// Oauth2 properties
38+
conf.set(restCatalogPrefix + "rest.auth.type", "oauth2");
39+
conf.set(restCatalogPrefix + "oauth2-server-uri", REST_CATALOG_EXTENSION.getOAuth2TokenEndpoint());
40+
conf.set(restCatalogPrefix + "credential", REST_CATALOG_EXTENSION.getOAuth2ClientCredential());
41+
}
42+
43+
@Override
44+
HiveRESTCatalogServerExtension getHiveRESTCatalogServerExtension() {
45+
return REST_CATALOG_EXTENSION;
46+
}
47+
}

itests/qtest-iceberg/pom.xml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,37 @@
480480
<artifactId>testcontainers</artifactId>
481481
<scope>test</scope>
482482
</dependency>
483+
<dependency>
484+
<groupId>org.keycloak</groupId>
485+
<artifactId>keycloak-admin-client</artifactId>
486+
<version>${keycloak.version}</version>
487+
<scope>test</scope>
488+
</dependency>
489+
<dependency>
490+
<groupId>jakarta.annotation</groupId>
491+
<artifactId>jakarta.annotation-api</artifactId>
492+
<version>2.1.1</version>
493+
</dependency>
494+
<dependency>
495+
<groupId>com.nimbusds</groupId>
496+
<artifactId>oauth2-oidc-sdk</artifactId>
497+
<version>${nimbus-oauth.version}</version>
498+
</dependency>
499+
<dependency>
500+
<groupId>jakarta.xml.bind</groupId>
501+
<artifactId>jakarta.xml.bind-api</artifactId>
502+
<version>4.0.4</version>
503+
</dependency>
504+
<dependency>
505+
<groupId>jakarta.activation</groupId>
506+
<artifactId>jakarta.activation-api</artifactId>
507+
<version>2.1.2</version>
508+
</dependency>
509+
<dependency>
510+
<groupId>org.glassfish.jaxb</groupId>
511+
<artifactId>jaxb-runtime</artifactId>
512+
<version>${jaxb-runtime.version}</version>
513+
</dependency>
483514
</dependencies>
484515
<build>
485516
<plugins>

itests/qtest-iceberg/src/test/java/org/apache/hadoop/hive/cli/HiveRESTCatalogServerExtension.java

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,43 +24,82 @@
2424
import org.apache.hadoop.hive.metastore.ServletSecurity.AuthType;
2525
import org.apache.hadoop.hive.metastore.conf.MetastoreConf;
2626
import org.apache.hadoop.hive.metastore.conf.MetastoreConf.ConfVars;
27+
import org.apache.iceberg.rest.extension.OAuth2AuthorizationServer;
2728
import org.apache.iceberg.rest.extension.RESTCatalogServer;
2829
import org.junit.rules.ExternalResource;
30+
import org.slf4j.Logger;
31+
import org.slf4j.LoggerFactory;
32+
33+
import java.util.HashMap;
34+
import java.util.Map;
2935

3036
public class HiveRESTCatalogServerExtension extends ExternalResource {
3137
private final Configuration conf;
38+
private final OAuth2AuthorizationServer authorizationServer;
3239
private final RESTCatalogServer restCatalogServer;
3340

34-
private HiveRESTCatalogServerExtension(AuthType authType, Class<? extends MetaStoreSchemaInfo> schemaInfoClass) {
41+
static final String HMS_ID = "hive-metastore";
42+
static final String HMS_SECRET = "hive-metastore-secret";
43+
44+
private static final Logger LOG = LoggerFactory.getLogger(HiveRESTCatalogServerExtension.class);
45+
46+
private HiveRESTCatalogServerExtension(AuthType authType, Class<? extends MetaStoreSchemaInfo> schemaInfoClass,
47+
Map<String, String> configurations) {
3548
this.conf = MetastoreConf.newMetastoreConf();
3649
MetastoreConf.setVar(conf, ConfVars.CATALOG_SERVLET_AUTH, authType.name());
50+
if (authType == AuthType.OAUTH2) {
51+
authorizationServer = new OAuth2AuthorizationServer();
52+
MetastoreConf.setVar(conf, ConfVars.CATALOG_SERVLET_AUTH, "oauth2");
53+
MetastoreConf.setVar(conf, ConfVars.CATALOG_SERVLET_AUTH_OAUTH2_CLIENT_ID, HMS_ID);
54+
MetastoreConf.setVar(conf, ConfVars.CATALOG_SERVLET_AUTH_OAUTH2_CLIENT_SECRET, HMS_SECRET);
55+
MetastoreConf.setVar(conf, ConfVars.CATALOG_SERVLET_AUTH_OAUTH2_AUDIENCE, HMS_ID);
56+
MetastoreConf.setVar(conf, ConfVars.CATALOG_SERVLET_AUTH_OAUTH2_PRINCIPAL_MAPPER_REGEX_FIELD, "email");
57+
MetastoreConf.setVar(conf, ConfVars.CATALOG_SERVLET_AUTH_OAUTH2_PRINCIPAL_MAPPER_REGEX_PATTERN,
58+
"(.*)@example.com");
59+
} else {
60+
authorizationServer = null;
61+
}
62+
configurations.forEach(conf::set);
3763
restCatalogServer = new RESTCatalogServer();
3864
if (schemaInfoClass != null) {
3965
restCatalogServer.setSchemaInfoClass(schemaInfoClass);
4066
}
4167
}
4268

43-
public Configuration getConf() {
44-
return conf;
45-
}
46-
4769
@Override
4870
protected void before() throws Throwable {
71+
if (authorizationServer != null) {
72+
authorizationServer.start();
73+
LOG.info("An authorization server {} started", authorizationServer.getIssuer());
74+
MetastoreConf.setVar(conf, ConfVars.CATALOG_SERVLET_AUTH_OAUTH2_ISSUER, authorizationServer.getIssuer());
75+
}
4976
restCatalogServer.start(conf);
5077
}
5178

5279
@Override
5380
protected void after() {
81+
if (authorizationServer != null) {
82+
authorizationServer.stop();
83+
}
5484
restCatalogServer.stop();
5585
}
5686

5787
public String getRestEndpoint() {
5888
return restCatalogServer.getRestEndpoint();
5989
}
6090

91+
public String getOAuth2TokenEndpoint() {
92+
return authorizationServer.getTokenEndpoint();
93+
}
94+
95+
public String getOAuth2ClientCredential() {
96+
return authorizationServer.getClientCredential();
97+
}
98+
6199
public static class Builder {
62100
private final AuthType authType;
63101
private Class<? extends MetaStoreSchemaInfo> metaStoreSchemaClass;
102+
private final Map<String, String> configurations = new HashMap<>();
64103

65104
private Builder(AuthType authType) {
66105
this.authType = authType;
@@ -72,7 +111,7 @@ public Builder addMetaStoreSchemaClassName(Class<? extends MetaStoreSchemaInfo>
72111
}
73112

74113
public HiveRESTCatalogServerExtension build() {
75-
return new HiveRESTCatalogServerExtension(authType, metaStoreSchemaClass);
114+
return new HiveRESTCatalogServerExtension(authType, metaStoreSchemaClass, configurations);
76115
}
77116
}
78117

0 commit comments

Comments
 (0)