| /** |
| * 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 |
| * <p> |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * <p> |
| * 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 com.google.common.base.Optional; |
| import org.apache.commons.lang3.RandomUtils; |
| import org.apache.hadoop.hdds.client.StandaloneReplicationConfig; |
| import org.apache.hadoop.hdds.conf.OzoneConfiguration; |
| import org.apache.hadoop.hdds.protocol.proto.HddsProtos; |
| import org.apache.hadoop.hdds.server.OzoneAdmins; |
| import org.apache.hadoop.hdds.utils.db.cache.CacheKey; |
| import org.apache.hadoop.hdds.utils.db.cache.CacheValue; |
| import org.apache.hadoop.ozone.OzoneAcl; |
| import org.apache.hadoop.ozone.om.BucketManager; |
| import org.apache.hadoop.ozone.om.KeyManager; |
| import org.apache.hadoop.ozone.om.OMMetadataManager; |
| import org.apache.hadoop.ozone.om.OmTestManagers; |
| import org.apache.hadoop.ozone.om.PrefixManager; |
| import org.apache.hadoop.ozone.om.VolumeManager; |
| import org.apache.hadoop.ozone.om.helpers.OmBucketInfo; |
| import org.apache.hadoop.ozone.om.helpers.OmKeyArgs; |
| import org.apache.hadoop.ozone.om.helpers.OmKeyInfo; |
| import org.apache.hadoop.ozone.om.helpers.OmVolumeArgs; |
| import org.apache.hadoop.ozone.om.helpers.OpenKeySession; |
| import org.apache.hadoop.ozone.om.helpers.OzoneAclUtil; |
| import org.apache.hadoop.ozone.om.helpers.BucketLayout; |
| import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol; |
| import org.apache.hadoop.ozone.om.request.OMRequestTestUtils; |
| import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType; |
| import org.apache.hadoop.security.UserGroupInformation; |
| import org.apache.hadoop.security.authentication.client.AuthenticationException; |
| import org.apache.ozone.test.GenericTestUtils; |
| import org.apache.ozone.test.tag.Flaky; |
| import org.junit.Assert; |
| import org.junit.BeforeClass; |
| import org.junit.Test; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.Collections; |
| import java.util.List; |
| |
| import static org.apache.hadoop.hdds.HddsConfigKeys.OZONE_METADATA_DIRS; |
| import static org.apache.hadoop.ozone.OzoneAcl.AclScope.ACCESS; |
| 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_ADMINISTRATORS; |
| import static org.apache.hadoop.ozone.OzoneConsts.OZONE_URI_DELIMITER; |
| import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLIdentityType.USER; |
| import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.ALL; |
| import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.CREATE; |
| import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.DELETE; |
| import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.LIST; |
| import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.READ; |
| import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.READ_ACL; |
| import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.WRITE; |
| import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.WRITE_ACL; |
| import static org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.BUCKET; |
| import static org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.KEY; |
| import static org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.VOLUME; |
| import static org.apache.hadoop.ozone.security.acl.OzoneObj.StoreType.OZONE; |
| |
| /** |
| * Test parent acl requirements when accessing children with native authorizer. |
| */ |
| public class TestParentAcl { |
| private static OzoneConfiguration ozConfig; |
| private static KeyManager keyManager; |
| private static VolumeManager volumeManager; |
| private static BucketManager bucketManager; |
| private static PrefixManager prefixManager; |
| private static OMMetadataManager metadataManager; |
| private static OzoneNativeAuthorizer nativeAuthorizer; |
| private static UserGroupInformation adminUgi; |
| private static UserGroupInformation testUgi, testUgi1; |
| private static OzoneManagerProtocol writeClient; |
| |
| @BeforeClass |
| public static void setup() throws IOException, AuthenticationException { |
| ozConfig = new OzoneConfiguration(); |
| ozConfig.set(OZONE_ACL_AUTHORIZER_CLASS, |
| OZONE_ACL_AUTHORIZER_CLASS_NATIVE); |
| File dir = GenericTestUtils.getRandomizedTestDir(); |
| ozConfig.set(OZONE_METADATA_DIRS, dir.toString()); |
| ozConfig.set(OZONE_ADMINISTRATORS, "om"); |
| |
| OmTestManagers omTestManagers = |
| new OmTestManagers(ozConfig); |
| metadataManager = omTestManagers.getMetadataManager(); |
| volumeManager = omTestManagers.getVolumeManager(); |
| bucketManager = omTestManagers.getBucketManager(); |
| prefixManager = omTestManagers.getPrefixManager(); |
| keyManager = omTestManagers.getKeyManager(); |
| writeClient = omTestManagers.getWriteClient(); |
| nativeAuthorizer = new OzoneNativeAuthorizer(volumeManager, bucketManager, |
| keyManager, prefixManager, |
| new OzoneAdmins(Collections.singletonList("om"))); |
| adminUgi = UserGroupInformation.createUserForTesting("om", |
| new String[]{"ozone"}); |
| testUgi = UserGroupInformation.createUserForTesting("testuser", |
| new String[]{"test"}); |
| testUgi1 = UserGroupInformation.createUserForTesting("testuser1", |
| new String[]{"test1"}); |
| } |
| |
| // Refined the parent context |
| // OP |CHILD |PARENT |
| |
| // CREATE NONE WRITE |
| // DELETE DELETE WRITE |
| // WRITE WRITE WRITE |
| // WRITE_ACL WRITE_ACL WRITE (V1 WRITE_ACL=>WRITE) |
| |
| // READ READ READ |
| // LIST LIST READ (V1 LIST=>READ) |
| // READ_ACL READ_ACL READ (V1 READ_ACL=>READ) |
| @Test |
| @Flaky("HDDS-6335") |
| public void testKeyAcl() |
| throws IOException { |
| OzoneObj keyObj; |
| int randomInt = RandomUtils.nextInt(); |
| String vol = "vol" + randomInt; |
| String buck = "bucket" + randomInt; |
| String key = "key" + randomInt; |
| |
| createVolume(vol); |
| createBucket(vol, buck); |
| keyObj = createKey(vol, buck, key); |
| |
| List<OzoneAcl> originalVolAcls = getVolumeAcls(vol); |
| List<OzoneAcl> originalBuckAcls = getBucketAcls(vol, buck); |
| List<OzoneAcl> originalKeyAcls = getBucketAcls(vol, buck); |
| |
| testParentChild(keyObj, WRITE, WRITE_ACL); |
| resetAcl(vol, originalVolAcls, buck, originalBuckAcls, |
| key, originalKeyAcls); |
| |
| testParentChild(keyObj, WRITE, DELETE); |
| resetAcl(vol, originalVolAcls, buck, originalBuckAcls, |
| key, originalKeyAcls); |
| |
| testParentChild(keyObj, READ, READ_ACL); |
| resetAcl(vol, originalVolAcls, buck, originalBuckAcls, |
| key, originalKeyAcls); |
| |
| testParentChild(keyObj, READ, LIST); |
| resetAcl(vol, originalVolAcls, buck, originalBuckAcls, |
| key, originalKeyAcls); |
| |
| testParentChild(keyObj, WRITE, CREATE); |
| resetAcl(vol, originalVolAcls, buck, originalBuckAcls, |
| key, originalKeyAcls); |
| |
| testParentChild(keyObj, WRITE, WRITE); |
| resetAcl(vol, originalVolAcls, buck, originalBuckAcls, |
| key, originalKeyAcls); |
| |
| testParentChild(keyObj, READ, READ); |
| resetAcl(vol, originalVolAcls, buck, originalBuckAcls, |
| key, originalKeyAcls); |
| } |
| |
| @Test |
| public void testBucketAcl() |
| throws IOException { |
| OzoneObj bucketObj; |
| int randomInt = RandomUtils.nextInt(); |
| String vol = "vol" + randomInt; |
| String buck = "bucket" + randomInt; |
| |
| createVolume(vol); |
| bucketObj = createBucket(vol, buck); |
| |
| List<OzoneAcl> originalVolAcls = getVolumeAcls(vol); |
| List<OzoneAcl> originalBuckAcls = getBucketAcls(vol, buck); |
| testParentChild(bucketObj, WRITE, WRITE_ACL); |
| resetAcl(vol, originalVolAcls, buck, originalBuckAcls, null, null); |
| |
| testParentChild(bucketObj, WRITE, DELETE); |
| resetAcl(vol, originalVolAcls, buck, originalBuckAcls, null, null); |
| |
| testParentChild(bucketObj, READ, READ_ACL); |
| resetAcl(vol, originalVolAcls, buck, originalBuckAcls, null, null); |
| |
| testParentChild(bucketObj, READ, LIST); |
| resetAcl(vol, originalVolAcls, buck, originalBuckAcls, null, null); |
| |
| testParentChild(bucketObj, WRITE, CREATE); |
| resetAcl(vol, originalVolAcls, buck, originalBuckAcls, null, null); |
| |
| testParentChild(bucketObj, READ, READ); |
| resetAcl(vol, originalVolAcls, buck, originalBuckAcls, null, null); |
| |
| testParentChild(bucketObj, WRITE, WRITE); |
| resetAcl(vol, originalVolAcls, buck, originalBuckAcls, null, null); |
| } |
| |
| private void resetAcl(String vol, List<OzoneAcl> volAcls, |
| String buck, List<OzoneAcl> buckAcls, |
| String key, List<OzoneAcl> keyAcls) throws IOException { |
| if (volAcls != null) { |
| setVolumeAcl(vol, volAcls); |
| } |
| |
| if (buckAcls != null) { |
| setBucketAcl(vol, buck, buckAcls); |
| } |
| |
| if (keyAcls != null) { |
| setKeyAcl(vol, buck, key, keyAcls); |
| } |
| } |
| |
| private void testParentChild(OzoneObj child, |
| ACLType parentAclType, ACLType childAclType) throws IOException { |
| |
| RequestContext requestContext = new RequestContext.Builder() |
| .setClientUgi(testUgi1) |
| .setAclType(USER) |
| .setAclRights(childAclType).build(); |
| |
| OzoneAcl childAcl = new OzoneAcl(USER, |
| testUgi1.getUserName(), childAclType, ACCESS); |
| |
| OzoneAcl parentAcl = new OzoneAcl(USER, |
| testUgi1.getUserName(), parentAclType, ACCESS); |
| |
| Assert.assertFalse(nativeAuthorizer.checkAccess(child, requestContext)); |
| if (child.getResourceType() == BUCKET) { |
| // add the bucket acl |
| addBucketAcl(child.getVolumeName(), child.getBucketName(), childAcl); |
| Assert.assertFalse(nativeAuthorizer.checkAccess(child, requestContext)); |
| |
| // add the volume acl (parent), now bucket access is allowed. |
| addVolumeAcl(child.getVolumeName(), parentAcl); |
| Assert.assertTrue(nativeAuthorizer.checkAccess(child, requestContext)); |
| |
| } else if (child.getResourceType() == KEY) { |
| // add key acl is not enough |
| addKeyAcl(child.getVolumeName(), child.getBucketName(), |
| child.getKeyName(), childAcl); |
| Assert.assertFalse(nativeAuthorizer.checkAccess(child, requestContext)); |
| |
| // add the bucket acl is not enough (parent) |
| addBucketAcl(child.getVolumeName(), child.getBucketName(), parentAcl); |
| Assert.assertFalse(nativeAuthorizer.checkAccess(child, requestContext)); |
| |
| // add the volume acl (grand-parent), now key access is allowed. |
| addVolumeAcl(child.getVolumeName(), parentAcl); |
| Assert.assertTrue(nativeAuthorizer.checkAccess(child, requestContext)); |
| } |
| } |
| |
| private void addVolumeAcl(String vol, OzoneAcl ozoneAcl) throws IOException { |
| String volumeKey = metadataManager.getVolumeKey(vol); |
| OmVolumeArgs omVolumeArgs = |
| metadataManager.getVolumeTable().get(volumeKey); |
| |
| omVolumeArgs.addAcl(ozoneAcl); |
| |
| metadataManager.getVolumeTable().addCacheEntry(new CacheKey<>(volumeKey), |
| new CacheValue<>(Optional.of(omVolumeArgs), 1L)); |
| } |
| |
| private List<OzoneAcl> getVolumeAcls(String vol) throws IOException { |
| String volumeKey = metadataManager.getVolumeKey(vol); |
| OmVolumeArgs omVolumeArgs = |
| metadataManager.getVolumeTable().get(volumeKey); |
| |
| return omVolumeArgs.getAcls(); |
| } |
| |
| private void setVolumeAcl(String vol, List<OzoneAcl> ozoneAcls) |
| throws IOException { |
| String volumeKey = metadataManager.getVolumeKey(vol); |
| OmVolumeArgs omVolumeArgs = metadataManager.getVolumeTable().get(volumeKey); |
| |
| omVolumeArgs.setAcls(ozoneAcls); |
| |
| metadataManager.getVolumeTable().addCacheEntry(new CacheKey<>(volumeKey), |
| new CacheValue<>(Optional.of(omVolumeArgs), 1L)); |
| } |
| |
| private void addKeyAcl(String vol, String buck, String key, |
| OzoneAcl ozoneAcl) throws IOException { |
| String objKey = metadataManager.getOzoneKey(vol, buck, key); |
| OmKeyInfo omKeyInfo = |
| metadataManager.getKeyTable(getBucketLayout()).get(objKey); |
| |
| omKeyInfo.addAcl(ozoneAcl); |
| |
| metadataManager.getKeyTable(getBucketLayout()) |
| .addCacheEntry(new CacheKey<>(objKey), |
| new CacheValue<>(Optional.of(omKeyInfo), 1L)); |
| } |
| |
| private void setKeyAcl(String vol, String buck, String key, |
| List<OzoneAcl> ozoneAcls) throws IOException { |
| String objKey = metadataManager.getOzoneKey(vol, buck, key); |
| OmKeyInfo omKeyInfo = |
| metadataManager.getKeyTable(getBucketLayout()).get(objKey); |
| omKeyInfo.setAcls(ozoneAcls); |
| |
| metadataManager.getKeyTable(getBucketLayout()) |
| .addCacheEntry(new CacheKey<>(objKey), |
| new CacheValue<>(Optional.of(omKeyInfo), 1L)); |
| } |
| |
| private void addBucketAcl(String vol, String buck, OzoneAcl ozoneAcl) |
| throws IOException { |
| String bucketKey = metadataManager.getBucketKey(vol, buck); |
| OmBucketInfo omBucketInfo = metadataManager.getBucketTable().get(bucketKey); |
| |
| omBucketInfo.addAcl(ozoneAcl); |
| |
| metadataManager.getBucketTable().addCacheEntry(new CacheKey<>(bucketKey), |
| new CacheValue<>(Optional.of(omBucketInfo), 1L)); |
| } |
| |
| private List<OzoneAcl> getBucketAcls(String vol, String buck) |
| throws IOException { |
| String bucketKey = metadataManager.getBucketKey(vol, buck); |
| OmBucketInfo omBucketInfo = metadataManager.getBucketTable().get(bucketKey); |
| |
| return omBucketInfo.getAcls(); |
| } |
| |
| private void setBucketAcl(String vol, String buck, |
| List<OzoneAcl> ozoneAcls) throws IOException { |
| String bucketKey = metadataManager.getBucketKey(vol, buck); |
| OmBucketInfo omBucketInfo = metadataManager.getBucketTable().get(bucketKey); |
| |
| omBucketInfo.setAcls(ozoneAcls); |
| |
| metadataManager.getBucketTable().addCacheEntry(new CacheKey<>(bucketKey), |
| new CacheValue<>(Optional.of(omBucketInfo), 1L)); |
| } |
| |
| private static OzoneObjInfo createVolume(String volumeName) |
| throws IOException { |
| OmVolumeArgs volumeArgs = OmVolumeArgs.newBuilder() |
| .setVolume(volumeName) |
| .setAdminName(adminUgi.getUserName()) |
| .setOwnerName(testUgi.getUserName()) |
| .build(); |
| OMRequestTestUtils.addVolumeToOM(metadataManager, volumeArgs); |
| return new OzoneObjInfo.Builder() |
| .setVolumeName(volumeName) |
| .setResType(VOLUME) |
| .setStoreType(OZONE) |
| .build(); |
| } |
| |
| private static OzoneObjInfo createBucket(String volumeName, String bucketName) |
| throws IOException { |
| OmBucketInfo bucketInfo = OmBucketInfo.newBuilder() |
| .setVolumeName(volumeName) |
| .setBucketName(bucketName) |
| .build(); |
| OMRequestTestUtils.addBucketToOM(metadataManager, bucketInfo); |
| return new OzoneObjInfo.Builder() |
| .setVolumeName(volumeName) |
| .setBucketName(bucketName) |
| .setResType(BUCKET) |
| .setStoreType(OZONE) |
| .build(); |
| } |
| |
| private OzoneObjInfo createKey(String volume, String bucket, String keyName) |
| throws IOException { |
| OmKeyArgs keyArgs = new OmKeyArgs.Builder() |
| .setVolumeName(volume) |
| .setBucketName(bucket) |
| .setKeyName(keyName) |
| .setReplicationConfig(StandaloneReplicationConfig.getInstance( |
| HddsProtos.ReplicationFactor.ONE)) |
| .setDataSize(0) |
| // here we give test ugi full access |
| .setAcls(OzoneAclUtil.getAclList(testUgi.getUserName(), |
| testUgi.getGroupNames(), ALL, ALL)) |
| .build(); |
| |
| |
| if (keyName.split(OZONE_URI_DELIMITER).length > 1) { |
| writeClient.createDirectory(keyArgs); |
| } else { |
| OpenKeySession keySession = writeClient.createFile(keyArgs, true, false); |
| keyArgs.setLocationInfoList( |
| keySession.getKeyInfo().getLatestVersionLocations() |
| .getLocationList()); |
| writeClient.commitKey(keyArgs, keySession.getId()); |
| } |
| |
| return new OzoneObjInfo.Builder() |
| .setVolumeName(volume) |
| .setBucketName(bucket) |
| .setKeyName(keyName) |
| .setResType(KEY) |
| .setStoreType(OZONE) |
| .build(); |
| } |
| |
| private BucketLayout getBucketLayout() { |
| return BucketLayout.DEFAULT; |
| } |
| } |