This is an automated email from the ASF dual-hosted git repository.
jojochuang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/master by this push:
new b58885e7baf Adding OM tests.
b58885e7baf is described below
commit b58885e7bafd71ed837ce757cff447f93c7147cc
Author: Wei-Chiu Chuang <[email protected]>
AuthorDate: Fri Aug 14 09:05:55 2026 -0700
Adding OM tests.
Change-Id: Ie5e6ceef61bd6ffa3e2eed30c7ccb3e9d718592a
---
.../ozone/om/TestGetDBUpdatesAuthorization.java | 137 +++++++++++++++++++++
.../org/apache/hadoop/ozone/om/OzoneManager.java | 27 ++++
2 files changed, 164 insertions(+)
diff --git
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestGetDBUpdatesAuthorization.java
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestGetDBUpdatesAuthorization.java
new file mode 100644
index 00000000000..906597647f2
--- /dev/null
+++
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestGetDBUpdatesAuthorization.java
@@ -0,0 +1,137 @@
+/*
+ * 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.hadoop.ozone.om;
+
+import static
org.apache.hadoop.hdds.security.SecurityConfig.OZONE_TEST_AUTHORIZATION_ENABLED;
+import static
org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ACL_AUTHORIZER_CLASS;
+import static
org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ACL_AUTHORIZER_CLASS_NATIVE;
+import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ACL_ENABLED;
+import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ADMINISTRATORS;
+import static
org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_READONLY_ADMINISTRATORS;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.security.PrivilegedExceptionAction;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.ozone.MiniOzoneCluster;
+import org.apache.hadoop.ozone.client.OzoneBucket;
+import org.apache.hadoop.ozone.client.OzoneClient;
+import org.apache.hadoop.ozone.client.OzoneClientFactory;
+import org.apache.hadoop.ozone.client.OzoneVolume;
+import org.apache.hadoop.ozone.client.io.OzoneOutputStream;
+import org.apache.hadoop.ozone.om.exceptions.OMException;
+import org.apache.hadoop.ozone.om.helpers.DBUpdates;
+import org.apache.hadoop.ozone.om.protocolPB.OmTransportFactory;
+import
org.apache.hadoop.ozone.om.protocolPB.OzoneManagerProtocolClientSideTranslatorPB;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DBUpdatesRequest;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.ratis.protocol.ClientId;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests authorization of the OzoneManager getDBUpdates RPC (OmClientProtocol
DBUpdates).
+ * getDBUpdates streams the raw RocksDB delta of the whole OM metadata DB and
backs
+ * OM->Recon replication, so it is restricted to admins and read-only admins,
like the
+ * other whole-system reads (listOpenFiles, getQuotaRepairStatus).
+ */
+public class TestGetDBUpdatesAuthorization {
+
+ private static MiniOzoneCluster cluster;
+ private static OzoneConfiguration conf;
+
+ private static final String VOL = "vol1";
+ private static final String BUCKET = "bucket1";
+ private static final String KEY = "key1";
+ private static final String RECON_PRINCIPAL = "reconsvc";
+
+ @BeforeAll
+ static void init() throws Exception {
+ conf = new OzoneConfiguration();
+ conf.setBoolean(OZONE_ACL_ENABLED, true);
+ conf.set(OZONE_ACL_AUTHORIZER_CLASS, OZONE_ACL_AUTHORIZER_CLASS_NATIVE);
+ conf.set(OZONE_ADMINISTRATORS, "admin");
+ conf.set(OZONE_READONLY_ADMINISTRATORS, RECON_PRINCIPAL);
+ // Make admin authorization effective without a KDC so the gate actually
runs.
+ conf.setBoolean(OZONE_TEST_AUTHORIZATION_ENABLED, true);
+ cluster = MiniOzoneCluster.newBuilder(conf).setNumDatanodes(3).build();
+ cluster.waitForClusterToBeReady();
+
+ UserGroupInformation.createUserForTesting("admin", new String[] {"admins"})
+ .doAs((PrivilegedExceptionAction<Void>) () -> {
+ try (OzoneClient c = OzoneClientFactory.getRpcClient(conf)) {
+ c.getObjectStore().createVolume(VOL);
+ OzoneVolume vol = c.getObjectStore().getVolume(VOL);
+ vol.createBucket(BUCKET);
+ OzoneBucket b = vol.getBucket(BUCKET);
+ byte[] data =
"hello".getBytes(java.nio.charset.StandardCharsets.UTF_8);
+ try (OzoneOutputStream os = b.createKey(KEY, data.length)) {
+ os.write(data);
+ }
+ }
+ return null;
+ });
+ }
+
+ @AfterAll
+ static void shutdown() {
+ if (cluster != null) {
+ cluster.shutdown();
+ }
+ }
+
+ private static DBUpdates getDBUpdates(UserGroupInformation user) throws
Exception {
+ return user.doAs((PrivilegedExceptionAction<DBUpdates>) () -> {
+ OzoneManagerProtocolClientSideTranslatorPB omClient =
+ new OzoneManagerProtocolClientSideTranslatorPB(
+ OmTransportFactory.create(conf, user, null),
+ ClientId.randomId().toString());
+ DBUpdatesRequest req = DBUpdatesRequest.newBuilder()
+ .setSequenceNumber(0)
+ .build();
+ return omClient.getDBUpdates(req);
+ });
+ }
+
+ @Test
+ void nonAdminIsDenied() {
+ UserGroupInformation nonAdmin =
+ UserGroupInformation.createUserForTesting("nonadmin", new String[]
{"users"});
+ OMException ex = assertThrows(OMException.class, () ->
getDBUpdates(nonAdmin));
+ assertEquals(OMException.ResultCodes.PERMISSION_DENIED, ex.getResult());
+ }
+
+ @Test
+ void adminIsAllowed() throws Exception {
+ UserGroupInformation admin =
+ UserGroupInformation.createUserForTesting("admin", new String[]
{"admins"});
+ DBUpdates updates = getDBUpdates(admin);
+ assertFalse(updates.getData().isEmpty());
+ }
+
+ @Test
+ void readOnlyAdminIsAllowed() throws Exception {
+ UserGroupInformation recon =
+ UserGroupInformation.createUserForTesting(RECON_PRINCIPAL, new
String[] {"recon"});
+ DBUpdates updates = getDBUpdates(recon);
+ assertTrue(updates.getData() != null && !updates.getData().isEmpty());
+ }
+}
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
index 6d46cb8e81e..09381bacb3d 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
@@ -4838,6 +4838,13 @@ public boolean isFilesystemSnapshotEnabled() {
public DBUpdates getDBUpdates(
DBUpdatesRequest dbUpdatesRequest)
throws IOException {
+ // getDBUpdates returns the raw RocksDB delta of the entire OM metadata DB
(all
+ // volume/bucket/key names, ACLs, block locations, tenant/S3-secret
state). It backs
+ // OM->Recon replication and is not a per-object client read, so restrict
it to admins
+ // and read-only admins (the Recon service principal is expected to be
one), consistent
+ // with the gating already applied to the other whole-system reads such as
listOpenFiles
+ // and getQuotaRepairStatus.
+ checkGetDBUpdatesPrivilege();
long limitCount = Long.MAX_VALUE;
if (dbUpdatesRequest.hasLimitCount()) {
limitCount = dbUpdatesRequest.getLimitCount();
@@ -4939,6 +4946,26 @@ private void checkAdminUserPrivilege(String operation)
throws IOException {
}
}
+ /**
+ * Authorize a getDBUpdates call, which returns the raw whole-DB metadata
delta.
+ * Allowed for full OM admins and read-only admins (the read-only-admin
allow-list is the
+ * mechanism intended to grant the Recon service principal read access to
the metadata feed
+ * without full admin rights). Only enforced when admin authorization is
enabled, matching
+ * {@link #checkAdminUserPrivilege(String)}.
+ */
+ private void checkGetDBUpdatesPrivilege() throws IOException {
+ // Skip check if authorization is disabled
+ if (!isAdminAuthorizationEnabled()) {
+ return;
+ }
+
+ final UserGroupInformation ugi = getRemoteUser();
+ if (!isAdmin(ugi) && !isReadOnlyAdmin(ugi)) {
+ throw new OMException("Only Ozone admins and read-only admins are
allowed to "
+ + "access the OM metadata database via getDBUpdates.",
PERMISSION_DENIED);
+ }
+ }
+
public boolean isS3Admin(UserGroupInformation callerUgi) {
return OzoneAdmins.isS3Admin(callerUgi, s3OzoneAdmins);
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]