From a903d4d3d096a27253476abc0e2359e9249903da Mon Sep 17 00:00:00 2001 From: Tsz-Wo Nicholas Sze Date: Mon, 14 Jul 2025 19:20:56 +0800 Subject: [PATCH] HDDS-13435. Add an OzoneManagerAuthorizer interface. --- .../security/acl/OzoneAuthorizerFactory.java | 44 +++++--------- .../security/acl/OzoneManagerAuthorizer.java | 30 ++++++++++ .../security/acl/OzoneNativeAuthorizer.java | 44 +++++--------- .../security/acl/TestOzoneAdministrators.java | 58 +++++++++++-------- 4 files changed, 93 insertions(+), 83 deletions(-) create mode 100644 hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/security/acl/OzoneManagerAuthorizer.java diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/security/acl/OzoneAuthorizerFactory.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/security/acl/OzoneAuthorizerFactory.java index 254df7506ea6..0a45cd789a2d 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/security/acl/OzoneAuthorizerFactory.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/security/acl/OzoneAuthorizerFactory.java @@ -28,11 +28,14 @@ import org.apache.hadoop.ozone.om.OmSnapshot; import org.apache.hadoop.ozone.om.OzoneManager; import org.apache.hadoop.ozone.om.PrefixManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Creates {@link IAccessAuthorizer} instances based on configuration. */ public final class OzoneAuthorizerFactory { + static final Logger LOG = LoggerFactory.getLogger(OzoneAuthorizerFactory.class); private OzoneAuthorizerFactory() { // no instances @@ -42,7 +45,7 @@ private OzoneAuthorizerFactory() { * @return authorizer instance for {@link OzoneManager} */ public static IAccessAuthorizer forOM(OzoneManager om) { - return create(om, om.getKeyManager(), om.getPrefixManager()); + return create(om, om.getKeyManager(), om.getPrefixManager(), "OM"); } /** @@ -53,7 +56,7 @@ public static IAccessAuthorizer forSnapshot( OzoneManager om, KeyManager keyManager, PrefixManager prefixManager ) { return om.getAccessAuthorizer().isNative() - ? create(om, keyManager, prefixManager) + ? create(om, keyManager, prefixManager, "Snapshot") : om.getAccessAuthorizer(); } @@ -61,9 +64,13 @@ public static IAccessAuthorizer forSnapshot( * Creates new instance (except for {@link OzoneAccessAuthorizer}, * which is a no-op authorizer. */ - private static IAccessAuthorizer create( - OzoneManager om, KeyManager km, PrefixManager pm - ) { + private static IAccessAuthorizer create(OzoneManager om, KeyManager km, PrefixManager pm, String name) { + final IAccessAuthorizer authorizer = createImpl(om, km, pm); + LOG.info("{}: Authorizer for {} is {}", om.getOMNodeId(), name, authorizer.getClass()); + return authorizer; + } + + private static IAccessAuthorizer createImpl(OzoneManager om, KeyManager km, PrefixManager pm) { if (!om.getAclsEnabled()) { return OzoneAccessAuthorizer.get(); } @@ -76,14 +83,13 @@ private static IAccessAuthorizer create( } if (OzoneNativeAuthorizer.class == clazz) { - final OzoneNativeAuthorizer authorizer = new OzoneNativeAuthorizer(); - return configure(authorizer, om, km, pm); + return new OzoneNativeAuthorizer().configure(om, km, pm); } final IAccessAuthorizer authorizer = newInstance(clazz, conf); - if (authorizer instanceof OzoneNativeAuthorizer) { - return configure((OzoneNativeAuthorizer) authorizer, om, km, pm); + if (authorizer instanceof OzoneManagerAuthorizer) { + return ((OzoneManagerAuthorizer) authorizer).configure(om, km, pm); } // If authorizer isn't native and shareable tmp dir is enabled, @@ -91,31 +97,13 @@ private static IAccessAuthorizer create( if (conf.getBoolean(OZONE_OM_ENABLE_OFS_SHARED_TMP_DIR, OZONE_OM_ENABLE_OFS_SHARED_TMP_DIR_DEFAULT)) { return new SharedTmpDirAuthorizer( - configure(new OzoneNativeAuthorizer(), om, km, pm), + new OzoneNativeAuthorizer().configure(om, km, pm), authorizer); } return authorizer; } - /** - * Configure {@link OzoneNativeAuthorizer}. - * @return same instance for convenience - */ - private static OzoneNativeAuthorizer configure( - OzoneNativeAuthorizer authorizer, - OzoneManager om, KeyManager km, PrefixManager pm - ) { - authorizer.setVolumeManager(om.getVolumeManager()); - authorizer.setBucketManager(om.getBucketManager()); - authorizer.setKeyManager(km); - authorizer.setPrefixManager(pm); - authorizer.setAdminCheck(om::isAdmin); - authorizer.setReadOnlyAdminCheck(om::isReadOnlyAdmin); - authorizer.setAllowListAllVolumes(om::getAllowListAllVolumes); - return authorizer; - } - private static Class authorizerClass( ConfigurationSource conf) { return conf.getClass(OZONE_ACL_AUTHORIZER_CLASS, diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/security/acl/OzoneManagerAuthorizer.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/security/acl/OzoneManagerAuthorizer.java new file mode 100644 index 000000000000..2fe8767bf092 --- /dev/null +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/security/acl/OzoneManagerAuthorizer.java @@ -0,0 +1,30 @@ +/* + * 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.security.acl; + +import org.apache.hadoop.ozone.om.KeyManager; +import org.apache.hadoop.ozone.om.OzoneManager; +import org.apache.hadoop.ozone.om.PrefixManager; + +/** + * A subinterface of {@link IAccessAuthorizer} specifically for Ozone Manager. + */ +public interface OzoneManagerAuthorizer extends IAccessAuthorizer { + /** Configure this authorizer. */ + OzoneManagerAuthorizer configure(OzoneManager om, KeyManager km, PrefixManager pm); +} diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/security/acl/OzoneNativeAuthorizer.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/security/acl/OzoneNativeAuthorizer.java index 84fe15f58e2f..48277025ba38 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/security/acl/OzoneNativeAuthorizer.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/security/acl/OzoneNativeAuthorizer.java @@ -19,7 +19,6 @@ import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.INVALID_REQUEST; -import com.google.common.annotations.VisibleForTesting; import java.util.Objects; import java.util.function.BooleanSupplier; import java.util.function.Predicate; @@ -30,6 +29,7 @@ import org.apache.hadoop.ozone.om.BucketManager; import org.apache.hadoop.ozone.om.KeyManager; import org.apache.hadoop.ozone.om.OzoneAclUtils; +import org.apache.hadoop.ozone.om.OzoneManager; import org.apache.hadoop.ozone.om.PrefixManager; import org.apache.hadoop.ozone.om.VolumeManager; import org.apache.hadoop.ozone.om.exceptions.OMException; @@ -42,7 +42,7 @@ */ @InterfaceAudience.LimitedPrivate({"HDFS", "Yarn", "Ranger", "Hive", "HBase"}) @InterfaceStability.Evolving -public class OzoneNativeAuthorizer implements IAccessAuthorizer { +public class OzoneNativeAuthorizer implements OzoneManagerAuthorizer { private static final Logger LOG = LoggerFactory.getLogger(OzoneNativeAuthorizer.class); @@ -189,30 +189,18 @@ public boolean checkAccess(IOzoneObj ozObject, RequestContext context) } } - public void setVolumeManager(VolumeManager volumeManager) { - this.volumeManager = volumeManager; - } - - public void setBucketManager(BucketManager bucketManager) { - this.bucketManager = bucketManager; - } - - public void setKeyManager(KeyManager keyManager) { - this.keyManager = keyManager; - } - - public void setPrefixManager(PrefixManager prefixManager) { - this.prefixManager = prefixManager; - } - - @VisibleForTesting - void setOzoneAdmins(OzoneAdmins admins) { - setAdminCheck(admins::isAdmin); - } - - @VisibleForTesting - void setOzoneReadOnlyAdmins(OzoneAdmins readOnlyAdmins) { - setReadOnlyAdminCheck(readOnlyAdmins::isAdmin); + @Override + public OzoneNativeAuthorizer configure(OzoneManager om, KeyManager km, PrefixManager pm) { + Objects.requireNonNull(om, "om == null"); + volumeManager = om.getVolumeManager(); + bucketManager = om.getBucketManager(); + allowListAllVolumes = om::getAllowListAllVolumes; + setAdminCheck(om::isAdmin); + setReadOnlyAdminCheck(om::isReadOnlyAdmin); + + keyManager = km; + prefixManager = pm; + return this; } public void setAdminCheck(Predicate check) { @@ -223,10 +211,6 @@ public void setReadOnlyAdminCheck(Predicate check) { readOnlyAdminCheck = Objects.requireNonNull(check, "read-only admin check"); } - public void setAllowListAllVolumes(BooleanSupplier allowListAllVolumes) { - this.allowListAllVolumes = Objects.requireNonNull(allowListAllVolumes, "allowListAllVolumes"); - } - public boolean getAllowListAllVolumes() { return allowListAllVolumes.getAsBoolean(); } diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/security/acl/TestOzoneAdministrators.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/security/acl/TestOzoneAdministrators.java index cdc246b21797..97fd4cdbfce4 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/security/acl/TestOzoneAdministrators.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/security/acl/TestOzoneAdministrators.java @@ -24,6 +24,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Collections; +import java.util.List; +import java.util.function.Predicate; import org.apache.hadoop.hdds.server.OzoneAdmins; import org.apache.hadoop.ozone.om.exceptions.OMException; import org.apache.hadoop.security.UserGroupInformation; @@ -57,6 +59,22 @@ public void testCreateVolume() throws Exception { } } + static Predicate newAdminUserPredicate(List adminUsernames) { + return new OzoneAdmins(adminUsernames, null)::isAdmin; + } + + static Predicate newAdminUserPredicate(String adminUsername) { + return newAdminUserPredicate(Collections.singletonList(adminUsername)); + } + + static Predicate newAdminGroupPredicate(List adminGroupNames) { + return new OzoneAdmins(null, adminGroupNames)::isAdmin; + } + + static Predicate newAdminGroupPredicate(String adminGroupName) { + return newAdminGroupPredicate(Collections.singletonList(adminGroupName)); + } + @Test public void testBucketOperation() throws OMException { UserGroupInformation.createUserForTesting("testuser", @@ -65,8 +83,7 @@ public void testBucketOperation() throws OMException { OzoneObj obj = getTestBucketobj("testbucket"); RequestContext context = getUserRequestContext("testuser", IAccessAuthorizer.ACLType.LIST); - nativeAuthorizer.setOzoneReadOnlyAdmins(new OzoneAdmins( - Collections.singletonList("testuser"), null)); + nativeAuthorizer.setReadOnlyAdminCheck(newAdminUserPredicate("testuser")); assertTrue(nativeAuthorizer.checkAccess(obj, context), "matching read only admins are allowed to preform" + "read operations"); @@ -91,8 +108,7 @@ public void testBucketOperation() throws OMException { assertThrows(NullPointerException.class, () -> nativeAuthorizer.checkAccess(obj, finalContext)); - nativeAuthorizer.setOzoneReadOnlyAdmins(new OzoneAdmins( - null, Collections.singletonList("testgroup"))); + nativeAuthorizer.setReadOnlyAdminCheck(newAdminGroupPredicate("testgroup")); context = getUserRequestContext("testuser", IAccessAuthorizer.ACLType.READ_ACL); assertTrue(nativeAuthorizer.checkAccess(obj, context), @@ -120,37 +136,34 @@ public void testListAllVolume() throws Exception { private void testAdminOperations(OzoneObj obj, RequestContext context) throws OMException { - nativeAuthorizer.setOzoneAdmins(new OzoneAdmins(Collections.emptyList())); + nativeAuthorizer.setAdminCheck(newAdminUserPredicate(Collections.emptyList())); assertFalse(nativeAuthorizer.checkAccess(obj, context), "empty" + " admin list disallow anyone to perform " + "admin operations"); - nativeAuthorizer.setOzoneAdmins(new OzoneAdmins( - Collections.singletonList(OZONE_ADMINISTRATORS_WILDCARD))); + nativeAuthorizer.setAdminCheck(newAdminUserPredicate(OZONE_ADMINISTRATORS_WILDCARD)); assertTrue(nativeAuthorizer.checkAccess(obj, context), "wildcard admin allows everyone to perform admin" + " operations"); - nativeAuthorizer.setOzoneAdmins(new OzoneAdmins( - Collections.singletonList("testuser"))); + nativeAuthorizer.setAdminCheck(newAdminUserPredicate("testuser")); assertTrue(nativeAuthorizer.checkAccess(obj, context), "matching admins are allowed to perform admin " + "operations"); - nativeAuthorizer.setOzoneAdmins(new OzoneAdmins( - asList(new String[]{"testuser2", "testuser"}))); + nativeAuthorizer.setAdminCheck(newAdminUserPredicate( + asList("testuser2", "testuser"))); assertTrue(nativeAuthorizer.checkAccess(obj, context), "matching admins are allowed to perform admin " + "operations"); - nativeAuthorizer.setOzoneAdmins(new OzoneAdmins( - asList(new String[]{"testuser2", "testuser3"}))); + nativeAuthorizer.setAdminCheck(newAdminUserPredicate( + asList("testuser2", "testuser3"))); assertFalse(nativeAuthorizer.checkAccess(obj, context), "mismatching admins are not allowed perform " + "admin operations"); - nativeAuthorizer.setOzoneReadOnlyAdmins(new OzoneAdmins( - Collections.singletonList("testuser"), null)); + nativeAuthorizer.setReadOnlyAdminCheck(newAdminUserPredicate("testuser")); if (context.getAclRights() == IAccessAuthorizer.ACLType.LIST) { assertTrue(nativeAuthorizer.checkAccess(obj, context), "matching read only user are allowed to preform" + @@ -161,8 +174,7 @@ private void testAdminOperations(OzoneObj obj, RequestContext context) "read operations"); } - nativeAuthorizer.setOzoneReadOnlyAdmins(new OzoneAdmins( - Collections.singletonList("testuser1"), null)); + nativeAuthorizer.setReadOnlyAdminCheck(newAdminUserPredicate("testuser1")); assertFalse(nativeAuthorizer.checkAccess(obj, context), "mismatching read only user are allowed to preform" + "read operations"); @@ -170,20 +182,17 @@ private void testAdminOperations(OzoneObj obj, RequestContext context) private void testGroupAdminOperations(OzoneObj obj, RequestContext context) throws OMException { - nativeAuthorizer.setOzoneAdmins( - new OzoneAdmins(null, asList("testgroup", "anothergroup"))); + nativeAuthorizer.setAdminCheck(newAdminGroupPredicate(asList("testgroup", "anothergroup"))); assertTrue(nativeAuthorizer.checkAccess(obj, context), "Users " + "from matching admin groups " + "are allowed to perform admin operations"); - nativeAuthorizer.setOzoneAdmins( - new OzoneAdmins(null, asList("wronggroup"))); + nativeAuthorizer.setAdminCheck(newAdminGroupPredicate("wronggroup")); assertFalse(nativeAuthorizer.checkAccess(obj, context), "Users" + " from mismatching admin groups " + "are allowed to perform admin operations"); - nativeAuthorizer.setOzoneReadOnlyAdmins(new OzoneAdmins( - null, Collections.singletonList("testgroup"))); + nativeAuthorizer.setReadOnlyAdminCheck(newAdminGroupPredicate("testgroup")); if (context.getAclRights() == IAccessAuthorizer.ACLType.LIST) { assertTrue(nativeAuthorizer.checkAccess(obj, context), "matching read only groups are allowed to preform" + @@ -194,8 +203,7 @@ private void testGroupAdminOperations(OzoneObj obj, RequestContext context) "preform read operations"); } - nativeAuthorizer.setOzoneReadOnlyAdmins(new OzoneAdmins( - null, Collections.singletonList("testgroup1"))); + nativeAuthorizer.setReadOnlyAdminCheck(newAdminGroupPredicate("testgroup1")); assertFalse(nativeAuthorizer.checkAccess(obj, context), "mismatching read only groups are allowed to preform" + "read operations");