This is an automated email from the ASF dual-hosted git repository.
avijayan pushed a commit to branch HDDS-4944
in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/HDDS-4944 by this push:
new dae8c0d HDDS-5834 [Multi-Tenant] Implement ListUsersInTenant, Remove
in memory maps. (#2759)
dae8c0d is described below
commit dae8c0df17f90c9c395fb306e5db5bed3ce44f26
Author: avijayanhwx <[email protected]>
AuthorDate: Thu Nov 4 10:41:57 2021 -0700
HDDS-5834 [Multi-Tenant] Implement ListUsersInTenant, Remove in memory
maps. (#2759)
---
.../java/org/apache/hadoop/ozone/OzoneConsts.java | 1 +
.../apache/hadoop/ozone/client/ObjectStore.java | 6 +
.../ozone/client/protocol/ClientProtocol.java | 11 +
.../apache/hadoop/ozone/client/rpc/RpcClient.java | 7 +
.../main/java/org/apache/hadoop/ozone/OmUtils.java | 1 +
.../org/apache/hadoop/ozone/audit/OMAction.java | 3 +-
.../hadoop/ozone/om/exceptions/OMException.java | 3 +-
.../hadoop/ozone/om/helpers/TenantUserList.java | 80 ++++++
...leTenantImpl.java => DefaultOzoneS3Tenant.java} | 20 +-
.../om/multitenant/OzoneTenantRolePrincipal.java | 2 +-
.../impl}/AccountNameSpaceImpl.java | 2 +-
.../impl/SingleVolumeTenantNamespace.java} | 34 ++-
.../impl}/package-info.java | 2 +-
.../ozone/om/protocol/OzoneManagerProtocol.java | 4 +
...OzoneManagerProtocolClientSideTranslatorPB.java | 19 ++
...estMultiTenantAccessAuthorizerRangerPlugin.java | 1 +
.../hadoop/ozone/shell/TestOzoneTenantShell.java | 35 ++-
.../src/main/proto/OmClientProtocol.proto | 23 ++
.../hadoop/ozone/om/OMMultiTenantManager.java | 45 ++--
.../hadoop/ozone/om/OMMultiTenantManagerImpl.java | 291 +++++++++++----------
.../org/apache/hadoop/ozone/om/OzoneManager.java | 65 ++++-
.../ozone/om/multitenant/CachedTenantInfo.java | 45 ++++
.../hadoop/ozone/om/multitenant}/package-info.java | 12 +-
.../s3/tenant/OMAssignUserToTenantRequest.java | 4 +-
.../request/s3/tenant/OMTenantCreateRequest.java | 18 +-
.../protocolPB/OzoneManagerRequestHandler.java | 24 ++
.../ozone/om/TestOMMultiTenantManagerImpl.java | 138 ++++++++++
.../om/request/key/TestOMKeyAclRequestWithFSO.java | 2 +-
.../s3/security/TestS3GetSecretRequest.java | 4 +-
.../ozone/shell/tenant/TenantListUsersHandler.java | 71 +++++
.../ozone/shell/tenant/TenantUserCommands.java | 3 +-
31 files changed, 748 insertions(+), 228 deletions(-)
diff --git
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConsts.java
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConsts.java
index 9eb96db..36f5e52 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConsts.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConsts.java
@@ -334,6 +334,7 @@ public final class OzoneConsts {
public static final String SOURCE_VOLUME = "sourceVolume";
public static final String SOURCE_BUCKET = "sourceBucket";
public static final String TENANT = "tenant";
+ public static final String USER_PREFIX = "userPrefix";
// For multi-tenancy
public static final String TENANT_NAME_USER_NAME_DELIMITER = "$";
diff --git
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/ObjectStore.java
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/ObjectStore.java
index ba6f353..41a471f 100644
---
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/ObjectStore.java
+++
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/ObjectStore.java
@@ -36,6 +36,7 @@ import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
import org.apache.hadoop.ozone.om.helpers.TenantInfoList;
import org.apache.hadoop.ozone.om.helpers.TenantUserInfoValue;
+import org.apache.hadoop.ozone.om.helpers.TenantUserList;
import org.apache.hadoop.ozone.security.OzoneTokenIdentifier;
import org.apache.hadoop.ozone.security.acl.OzoneObj;
import org.apache.hadoop.security.UserGroupInformation;
@@ -242,6 +243,11 @@ public class ObjectStore {
proxy.tenantRevokeAdmin(accessId, tenantName);
}
+ public TenantUserList listUsersInTenant(String tenantName, String userPrefix)
+ throws IOException {
+ return proxy.listUsersInTenant(tenantName, userPrefix);
+ }
+
/**
* Get tenant info for a user.
* @param userPrincipal Kerberos principal of a user.
diff --git
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
index 3845250..9eec9d0 100644
---
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
+++
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
@@ -49,6 +49,7 @@ import org.apache.hadoop.ozone.om.helpers.RepeatedOmKeyInfo;
import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
import org.apache.hadoop.ozone.om.helpers.TenantInfoList;
import org.apache.hadoop.ozone.om.helpers.TenantUserInfoValue;
+import org.apache.hadoop.ozone.om.helpers.TenantUserList;
import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRoleInfo;
import org.apache.hadoop.ozone.security.OzoneTokenIdentifier;
@@ -627,6 +628,16 @@ public interface ClientProtocol {
throws IOException;
/**
+ * Get List of users in a tenant.
+ * @param tenantName tenant name
+ * @param prefix optional prefix
+ * @return List of username, accessIds in tenant.
+ * @throws IOException on server error.
+ */
+ TenantUserList listUsersInTenant(String tenantName, String prefix)
+ throws IOException;
+
+ /**
* List tenants.
* @return TenantInfoList
* @throws IOException
diff --git
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
index eabbfbb..aebfff0 100644
---
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
+++
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
@@ -106,6 +106,7 @@ import org.apache.hadoop.ozone.om.helpers.ServiceInfo;
import org.apache.hadoop.ozone.om.helpers.ServiceInfoEx;
import org.apache.hadoop.ozone.om.helpers.TenantInfoList;
import org.apache.hadoop.ozone.om.helpers.TenantUserInfoValue;
+import org.apache.hadoop.ozone.om.helpers.TenantUserList;
import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol;
import org.apache.hadoop.ozone.om.protocolPB.OmTransport;
import org.apache.hadoop.ozone.om.protocolPB.OmTransportFactory;
@@ -734,6 +735,12 @@ public class RpcClient implements ClientProtocol {
}
@Override
+ public TenantUserList listUsersInTenant(String tenantName, String prefix)
+ throws IOException {
+ return ozoneManagerClient.listUsersInTenant(tenantName, prefix);
+ }
+
+ @Override
public void setBucketVersioning(
String volumeName, String bucketName, Boolean versioning)
throws IOException {
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
index 8e80634..a24e770 100644
--- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
@@ -268,6 +268,7 @@ public final class OmUtils {
case GetS3Volume:
case ListTenant:
case TenantGetUserInfo:
+ case TenantListUser:
return true;
case CreateVolume:
case SetVolumeProperty:
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java
index 5d37413..c95f74d 100644
---
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java
+++
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java
@@ -83,7 +83,8 @@ public enum OMAction implements AuditAction {
TENANT_REVOKE_USER_ACCESSID,
TENANT_ASSIGN_ADMIN,
- TENANT_REVOKE_ADMIN;
+ TENANT_REVOKE_ADMIN,
+ TENANT_LIST_USER;
@Override
public String getAction() {
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java
index 7b8502e..a049a20 100644
---
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java
+++
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java
@@ -251,6 +251,7 @@ public class OMException extends IOException {
TENANT_USER_ACCESSID_NOT_FOUND,
TENANT_USER_ACCESSID_ALREADY_EXISTS,
INVALID_TENANT_USER_NAME,
- INVALID_ACCESSID
+ INVALID_ACCESSID,
+ TENANT_AUTHORIZER_ERROR
}
}
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/TenantUserList.java
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/TenantUserList.java
new file mode 100644
index 0000000..acdaa7f
--- /dev/null
+++
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/TenantUserList.java
@@ -0,0 +1,80 @@
+/*
+ * 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.helpers;
+
+import java.util.List;
+import java.util.Objects;
+
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantListUserResponse;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantUserAccessId;
+
+/**
+ * Class to encapsulate the list of users and corresponding accessIds
+ * associated with a tenant.
+ */
+public class TenantUserList {
+
+ private final String tenantName;
+
+ private final List<TenantUserAccessId> userAccessIds;
+
+
+ public TenantUserList(String tenantName,
+ List<TenantUserAccessId> userAccessIds) {
+ this.tenantName = tenantName;
+ this.userAccessIds = userAccessIds;
+ }
+
+ public String getTenantName() {
+ return tenantName;
+ }
+
+ public List<TenantUserAccessId> getUserAccessIds() {
+ return userAccessIds;
+ }
+
+ public static TenantUserList fromProtobuf(TenantListUserResponse response) {
+ return new TenantUserList(response.getTenantName(),
+ response.getUserAccessIdInfoList());
+ }
+
+ @Override
+ public String toString() {
+ return "tenantName=" + tenantName +
+ "\nuserAccessIds=" + userAccessIds;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ TenantUserList that = (TenantUserList) o;
+ return tenantName.equals(that.tenantName) &&
+ userAccessIds.equals(that.userAccessIds);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(tenantName, userAccessIds);
+ }
+}
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/CephCompatibleTenantImpl.java
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/DefaultOzoneS3Tenant.java
similarity index 73%
rename from
hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/CephCompatibleTenantImpl.java
rename to
hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/DefaultOzoneS3Tenant.java
index 03d2b6a..48685f2 100644
---
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/CephCompatibleTenantImpl.java
+++
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/DefaultOzoneS3Tenant.java
@@ -17,38 +17,28 @@
*/
package org.apache.hadoop.ozone.om.multitenant;
-import static
org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.VOLUME;
-import static org.apache.hadoop.ozone.security.acl.OzoneObj.StoreType.OZONE;
-
import java.util.ArrayList;
import java.util.List;
-import org.apache.hadoop.ozone.om.multitenantImpl.AccountNameSpaceImpl;
-import org.apache.hadoop.ozone.om.multitenantImpl.BucketNameSpaceImpl;
-import org.apache.hadoop.ozone.security.acl.OzoneObj;
-import org.apache.hadoop.ozone.security.acl.OzoneObjInfo;
+import org.apache.hadoop.ozone.om.multitenant.impl.AccountNameSpaceImpl;
+import org.apache.hadoop.ozone.om.multitenant.impl.SingleVolumeTenantNamespace;
/**
* Implements Tenant.
*/
-public class CephCompatibleTenantImpl implements Tenant {
+public class DefaultOzoneS3Tenant implements Tenant {
private final String tenantID;
private List<String> tenantRoleIds;
private List<AccessPolicy> accessPolicies;
private final AccountNameSpace accountNameSpace;
private final BucketNameSpace bucketNameSpace;
- public CephCompatibleTenantImpl(String id) {
+ public DefaultOzoneS3Tenant(String id) {
tenantID = id;
accessPolicies = new ArrayList<>();
tenantRoleIds = new ArrayList<>();
accountNameSpace = new AccountNameSpaceImpl(id);
- bucketNameSpace = new BucketNameSpaceImpl(id);
- OzoneObj volume = new OzoneObjInfo.Builder()
- .setResType(VOLUME)
- .setStoreType(OZONE)
- .setVolumeName(bucketNameSpace.getBucketNameSpaceID()).build();
- bucketNameSpace.addBucketNameSpaceObject(volume);
+ bucketNameSpace = new SingleVolumeTenantNamespace(id);
}
@Override
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/OzoneTenantRolePrincipal.java
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/OzoneTenantRolePrincipal.java
index 3aa3e3d..7f2e651 100644
---
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/OzoneTenantRolePrincipal.java
+++
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/OzoneTenantRolePrincipal.java
@@ -53,4 +53,4 @@ public final class OzoneTenantRolePrincipal implements
Principal {
public String getName() {
return tenantID + OzoneConsts.TENANT_NAME_ROLE_DELIMITER + roleName;
}
-}
+}
\ No newline at end of file
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenantImpl/AccountNameSpaceImpl.java
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/impl/AccountNameSpaceImpl.java
similarity index 96%
rename from
hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenantImpl/AccountNameSpaceImpl.java
rename to
hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/impl/AccountNameSpaceImpl.java
index 93d84c2..e689b22 100644
---
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenantImpl/AccountNameSpaceImpl.java
+++
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/impl/AccountNameSpaceImpl.java
@@ -15,7 +15,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.hadoop.ozone.om.multitenantImpl;
+package org.apache.hadoop.ozone.om.multitenant.impl;
import org.apache.hadoop.hdds.client.OzoneQuota;
import org.apache.hadoop.hdds.fs.SpaceUsageSource;
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenantImpl/BucketNameSpaceImpl.java
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/impl/SingleVolumeTenantNamespace.java
similarity index 59%
rename from
hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenantImpl/BucketNameSpaceImpl.java
rename to
hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/impl/SingleVolumeTenantNamespace.java
index 88f7f36..df343c4 100644
---
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenantImpl/BucketNameSpaceImpl.java
+++
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/impl/SingleVolumeTenantNamespace.java
@@ -15,26 +15,39 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.hadoop.ozone.om.multitenantImpl;
+package org.apache.hadoop.ozone.om.multitenant.impl;
+
+import static
org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.VOLUME;
+import static org.apache.hadoop.ozone.security.acl.OzoneObj.StoreType.OZONE;
-import java.util.ArrayList;
import java.util.List;
import org.apache.hadoop.hdds.client.OzoneQuota;
import org.apache.hadoop.hdds.fs.SpaceUsageSource;
import org.apache.hadoop.ozone.om.multitenant.BucketNameSpace;
import org.apache.hadoop.ozone.security.acl.OzoneObj;
+import org.apache.hadoop.ozone.security.acl.OzoneObjInfo;
+
+import com.google.common.collect.ImmutableList;
/**
- * Implements BucketNameSpace.
+ * Implements BucketNameSpace which allows exactly ONE VOLUME.
*/
-public class BucketNameSpaceImpl implements BucketNameSpace {
+public class SingleVolumeTenantNamespace implements BucketNameSpace {
+ public static final String BUCKET_NS_PREFIX = "BucketNS-";
private final String bucketNameSpaceID;
- private List<OzoneObj> bucketNameSpaceObjects;
+ private final List<OzoneObj> nsObjects;
+
+ public SingleVolumeTenantNamespace(String id) {
+ this(id, id);
+ }
- public BucketNameSpaceImpl(String id) {
- bucketNameSpaceID = id;
- bucketNameSpaceObjects = new ArrayList<>();
+ public SingleVolumeTenantNamespace(String id, String volumeName) {
+ this.bucketNameSpaceID = BUCKET_NS_PREFIX + id;
+ this.nsObjects = ImmutableList.of(new OzoneObjInfo.Builder()
+ .setResType(VOLUME)
+ .setStoreType(OZONE)
+ .setVolumeName(volumeName).build());
}
@Override
@@ -44,12 +57,13 @@ public class BucketNameSpaceImpl implements BucketNameSpace
{
@Override
public List<OzoneObj> getBucketNameSpaceObjects() {
- return bucketNameSpaceObjects;
+ return nsObjects;
}
@Override
public void addBucketNameSpaceObject(OzoneObj e) {
- bucketNameSpaceObjects.add(e);
+ throw new UnsupportedOperationException("Cannot add an object to a single"
+
+ " SingleVolumeTenantNamespace.");
}
@Override
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenantImpl/package-info.java
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/impl/package-info.java
similarity index 94%
copy from
hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenantImpl/package-info.java
copy to
hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/impl/package-info.java
index 0b46716..60429c3 100644
---
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenantImpl/package-info.java
+++
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/impl/package-info.java
@@ -20,4 +20,4 @@
/**
* Package contains classes related to Ozone multi-tenant implementation.
*/
-package org.apache.hadoop.ozone.om.multitenantImpl;
+package org.apache.hadoop.ozone.om.multitenant.impl;
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerProtocol.java
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerProtocol.java
index 9063dd5..06b328d 100644
---
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerProtocol.java
+++
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerProtocol.java
@@ -49,6 +49,7 @@ import org.apache.hadoop.ozone.om.helpers.ServiceInfo;
import org.apache.hadoop.ozone.om.helpers.ServiceInfoEx;
import org.apache.hadoop.ozone.om.helpers.TenantInfoList;
import org.apache.hadoop.ozone.om.helpers.TenantUserInfoValue;
+import org.apache.hadoop.ozone.om.helpers.TenantUserList;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OzoneAclInfo;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse;
@@ -513,6 +514,9 @@ public interface OzoneManagerProtocol
TenantUserInfoValue tenantGetUserInfo(String userPrincipal)
throws IOException;
+ TenantUserList listUsersInTenant(String tenantName, String prefix)
+ throws IOException;
+
/**
* List tenants.
* @return TenantInfoList
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
index b01fe3a..168635e 100644
---
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
+++
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
@@ -58,6 +58,7 @@ import org.apache.hadoop.ozone.om.helpers.ServiceInfo;
import org.apache.hadoop.ozone.om.helpers.ServiceInfoEx;
import org.apache.hadoop.ozone.om.helpers.TenantInfoList;
import org.apache.hadoop.ozone.om.helpers.TenantUserInfoValue;
+import org.apache.hadoop.ozone.om.helpers.TenantUserList;
import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.AddAclRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.AddAclResponse;
@@ -1014,6 +1015,24 @@ public final class
OzoneManagerProtocolClientSideTranslatorPB
}
@Override
+ public TenantUserList listUsersInTenant(String tenantName, String prefix)
+ throws IOException {
+ TenantListUserRequest.Builder builder =
+ TenantListUserRequest.newBuilder().setTenantName(tenantName);
+ if (prefix != null) {
+ builder.setPrefix(prefix);
+ }
+ TenantListUserRequest request = builder.build();
+
+ final OMRequest omRequest = createOMRequest(Type.TenantListUser)
+ .setTenantListUserRequest(request).build();
+ final OMResponse response = submitRequest(omRequest);
+ final TenantListUserResponse resp =
+ handleError(response).getTenantListUserResponse();
+ return TenantUserList.fromProtobuf(resp);
+ }
+
+ @Override
public OmVolumeArgs getS3Volume(String accessID) throws IOException {
final GetS3VolumeRequest request = GetS3VolumeRequest.newBuilder()
.setAccessID(accessID)
diff --git
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantAccessAuthorizerRangerPlugin.java
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantAccessAuthorizerRangerPlugin.java
index 7532905..0eb52f9 100644
---
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantAccessAuthorizerRangerPlugin.java
+++
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantAccessAuthorizerRangerPlugin.java
@@ -230,6 +230,7 @@ public class TestMultiTenantAccessAuthorizerRangerPlugin {
return tenantVolumeAccessPolicy;
}
+
// TODO: REMOVE THIS?
private AccessPolicy allowAccessBucketPolicy(String vol, String bucketName,
String tenant) throws IOException {
diff --git
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneTenantShell.java
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneTenantShell.java
index 8204273..4556d4f 100644
---
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneTenantShell.java
+++
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneTenantShell.java
@@ -228,7 +228,7 @@ public class TestOzoneTenantShell {
* was thrown.
*/
private void executeWithError(OzoneShell shell, String[] args,
- String expectedError) {
+ String expectedError) {
if (Strings.isNullOrEmpty(expectedError)) {
execute(shell, args);
} else {
@@ -315,7 +315,7 @@ public class TestOzoneTenantShell {
* Helper function that checks command output AND clears it.
*/
private void checkOutput(ByteArrayOutputStream stream, String stringToMatch,
- boolean exactMatch) throws IOException {
+ boolean exactMatch) throws IOException {
stream.flush();
final String str = stream.toString(DEFAULT_ENCODING);
checkOutput(str, stringToMatch, exactMatch);
@@ -323,7 +323,7 @@ public class TestOzoneTenantShell {
}
private void checkOutput(String str, String stringToMatch,
- boolean exactMatch) {
+ boolean exactMatch) {
if (exactMatch) {
Assert.assertEquals(stringToMatch, str);
} else {
@@ -523,4 +523,31 @@ public class TestOzoneTenantShell {
checkOutput(err, "Revoked accessId", false);
}
-}
+ private void testListTenantUsers() throws IOException {
+ executeHA(tenantShell, new String[] {
+ "user", "assign", "[email protected]", "--tenant=research"});
+ checkOutput(out, "export AWS_ACCESS_KEY_ID='[email protected]'\n"
+ + "export AWS_SECRET_ACCESS_KEY='", false);
+ checkOutput(err, "Assigned '[email protected]' to 'research'" +
+ " with accessId '[email protected]'.\n", true);
+
+ executeHA(tenantShell, new String[] {
+ "user", "list", "--tenant=research"});
+ checkOutput(out,
+ "- User '[email protected]' with accessId '[email protected]'\n"
+ + "- User '[email protected]' with accessId
'research$alice@EXAMPLE"
+ + ".COM'\n", true);
+ checkOutput(err, "", true);
+
+ executeHA(tenantShell, new String[] {
+ "user", "list", "--tenant=research", "--prefix=b"});
+ checkOutput(out, "- User '[email protected]' with accessId " +
+ "'[email protected]'\n", true);
+ checkOutput(err, "", true);
+
+ executeHA(tenantShell, new String[] {
+ "user", "list", "--tenant=unknown"});
+ checkOutput(err, "Failed to Get Users in tenant 'unknown': " +
+ "Tenant 'unknown' not found!\n", true);
+ }
+}
\ No newline at end of file
diff --git
a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
index 6867247..2cb1c66 100644
--- a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
+++ b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
@@ -116,6 +116,7 @@ enum Type {
TenantRevokeAdmin = 102;
GetS3Volume = 104;
+ TenantListUser = 105;
}
message OMRequest {
@@ -212,6 +213,7 @@ message OMRequest {
optional TenantRevokeAdminRequest TenantRevokeAdminRequest =
102;
optional GetS3VolumeRequest getS3VolumeRequest =
104;
+ optional TenantListUserRequest tenantListUserRequest =
105;
}
message OMResponse {
@@ -303,6 +305,7 @@ message OMResponse {
optional TenantRevokeAdminResponse TenantRevokeAdminResponse =
102;
optional GetS3VolumeResponse getS3VolumeResponse =
104;
+ optional TenantListUserResponse tenantListUserResponse =
105;
}
enum Status {
@@ -406,6 +409,7 @@ enum Status {
TENANT_USER_ACCESSID_ALREADY_EXISTS = 79; // TODO: Remove if not used
INVALID_TENANT_USER_NAME = 80;
INVALID_ACCESSID = 81;
+ TENANT_AUTHORIZER_ERROR = 82;
}
/**
@@ -1386,6 +1390,14 @@ message TenantInfo {
optional string bucketPolicyGroupName = 5;
}
+message TenantUserAccessId {
+ optional string user = 1;
+ optional string accessId = 2;
+ optional bool isAdmin = 3;
+ optional bool isDelegatedAdmin = 4;
+}
+
+
message ListTenantRequest {
}
@@ -1399,11 +1411,22 @@ message TenantGetUserInfoRequest {
optional string userPrincipal = 1;
}
+message TenantListUserRequest {
+ optional string tenantName = 1;
+ optional string prefix = 2;
+}
+
message TenantGetUserInfoResponse {
optional bool success = 1;
optional TenantUserInfo tenantUserInfo = 2;
}
+message TenantListUserResponse {
+ optional bool success = 1;
+ optional string tenantName = 2;
+ repeated TenantUserAccessId userAccessIdInfo = 3;
+}
+
message TenantUserInfo {
optional string userPrincipal = 1;
repeated TenantAccessIdInfo accessIdInfo = 2;
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManager.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManager.java
index 5363e3b..c4d1793 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManager.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManager.java
@@ -20,7 +20,7 @@ import java.io.IOException;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
-import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.ozone.om.helpers.TenantUserList;
import org.apache.hadoop.ozone.om.multitenant.AccessPolicy;
import org.apache.hadoop.ozone.om.multitenant.AccountNameSpace;
import org.apache.hadoop.ozone.om.multitenant.BucketNameSpace;
@@ -31,8 +31,8 @@ import org.apache.http.auth.BasicUserPrincipal;
* OM MultiTenant manager interface.
*/
public interface OMMultiTenantManager {
- /**
- * Start multi-tenant manager. Performs initialization e.g.
+ /*
+ * Init multi-tenant manager. Performs initialization e.g.
* - Initialize Multi-Tenant-Gatekeeper-Plugin
* - Validate Multi-Tenant Bucket-NameSpaces
* - Validate Multi-Tenant Account-NameSpaces
@@ -44,15 +44,14 @@ public interface OMMultiTenantManager {
* . OM-DB state <-in-sync-> IMultiTenantGateKeeperPluginState
* . OM DB state is always the source of truth.
*
- * @param configuration
* @throws IOException
*/
- void start(OzoneConfiguration configuration) throws IOException;
-
- /**
- * Stop multi-tenant manager.
- */
- void stop() throws Exception;
+// void start() throws IOException;
+//
+// /**
+// * Stop multi-tenant manager.
+// */
+// void stop() throws Exception;
/**
* Returns the corresponding OzoneManager instance.
@@ -67,7 +66,8 @@ public interface OMMultiTenantManager {
* @param tenantID
* @return Tenant interface.
*/
- Tenant createTenant(String tenantID) throws IOException;
+ Tenant createTenantAccessInAuthorizer(String tenantID) throws IOException;
+
/**
* Given a TenantID String, Return Tenant Interface. If the Tenant doesn't
@@ -106,7 +106,7 @@ public interface OMMultiTenantManager {
* @return
* @throws IOException
*/
- void destroyTenant(Tenant tenant) throws Exception;
+ void removeTenantAccessFromAuthorizer(Tenant tenant) throws Exception;
/**
@@ -121,15 +121,12 @@ public interface OMMultiTenantManager {
String accessID) throws IOException;
/**
- * Given a user, destroys all state associated with that user.
- * This is different from deactivateUser().
+ * Revoke user accessId.
* @param accessID
- * @return
* @throws IOException
*/
void revokeUserAccessId(String accessID) throws IOException;
-
/**
* Given an accessId, return kerberos user name for the tenant user.
*/
@@ -160,6 +157,22 @@ public interface OMMultiTenantManager {
void deactivateUser(String accessID) throws IOException;
/**
+ * Check if a user is a tenant Admin.
+ * @param user user name.
+ * @param tenantName tenant name.
+ * @return
+ */
+ boolean isTenantAdmin(String user, String tenantName);
+
+ /**
+ * List all the user & accessIDs of all users that belong to this Tenant.
+ * @param tenantID
+ * @return List of users
+ */
+ TenantUserList listUsersInTenant(String tenantID, String prefix)
+ throws IOException;
+
+ /**
* List all the access IDs of all users that belong to this Tenant.
* @param tenantID
* @return List of users
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManagerImpl.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManagerImpl.java
index b82052e..1ffd6b4 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManagerImpl.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManagerImpl.java
@@ -17,6 +17,8 @@
*/
package org.apache.hadoop.ozone.om;
+import static
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.INVALID_ACCESSID;
+import static
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.TENANT_AUTHORIZER_ERROR;
import static
org.apache.hadoop.ozone.om.multitenant.AccessPolicy.AccessGrantType.ALLOW;
import static
org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.ALL;
import static
org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.CREATE;
@@ -30,22 +32,28 @@ import static
org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.VOLUME;
import static org.apache.hadoop.ozone.security.acl.OzoneObj.StoreType.OZONE;
import java.io.IOException;
-import java.util.Arrays;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-import com.google.common.annotations.VisibleForTesting;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+
import org.apache.commons.lang3.tuple.Pair;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdds.utils.db.Table;
+import org.apache.hadoop.hdds.utils.db.Table.KeyValue;
+import org.apache.hadoop.hdds.utils.db.TableIterator;
+import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.om.helpers.OmDBAccessIdInfo;
+import org.apache.hadoop.ozone.om.helpers.TenantUserList;
import org.apache.hadoop.ozone.om.multitenant.AccessPolicy;
import org.apache.hadoop.ozone.om.multitenant.AccountNameSpace;
import org.apache.hadoop.ozone.om.multitenant.BucketNameSpace;
-import org.apache.hadoop.ozone.om.multitenant.CephCompatibleTenantImpl;
+import org.apache.hadoop.ozone.om.multitenant.CachedTenantInfo;
+import org.apache.hadoop.ozone.om.multitenant.DefaultOzoneS3Tenant;
import org.apache.hadoop.ozone.om.multitenant.MultiTenantAccessAuthorizer;
import
org.apache.hadoop.ozone.om.multitenant.MultiTenantAccessAuthorizerDummyPlugin;
import
org.apache.hadoop.ozone.om.multitenant.MultiTenantAccessAuthorizerRangerPlugin;
@@ -53,6 +61,7 @@ import
org.apache.hadoop.ozone.om.multitenant.OzoneOwnerPrincipal;
import org.apache.hadoop.ozone.om.multitenant.OzoneTenantRolePrincipal;
import org.apache.hadoop.ozone.om.multitenant.RangerAccessPolicy;
import org.apache.hadoop.ozone.om.multitenant.Tenant;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantUserAccessId;
import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType;
import org.apache.hadoop.ozone.security.acl.OzoneObj;
import org.apache.hadoop.ozone.security.acl.OzoneObjInfo;
@@ -60,6 +69,7 @@ import org.apache.http.auth.BasicUserPrincipal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
/**
@@ -74,103 +84,47 @@ public class OMMultiTenantManagerImpl implements
OMMultiTenantManager {
// Internal dev flag to skip Ranger communication.
public static final String OZONE_OM_TENANT_DEV_SKIP_RANGER =
"ozone.om.tenant.dev.skip.ranger";
- private final boolean devSkipRanger;
private MultiTenantAccessAuthorizer authorizer;
private final OMMetadataManager omMetadataManager;
private final OzoneConfiguration conf;
private final ReentrantReadWriteLock controlPathLock;
-
- // The following Mappings maintain all of the multi-tenancy states.
- // These mappings needs to have their persistent counterpart in OM tables.
- // Long term, we can bring those tables here as part of multi-tenant-Manager
- // and not mixing up things with the rest of the OM. And thus giving this
- // module a clean separation from the rest of the OM.
-
- // key : tenantName, value : TenantInfo
- private final Map<String, Tenant> inMemoryTenantNameToTenantInfoMap;
-
- // This Mapping maintains all policies for all tenants
- // key = tenantName
- // value = list of all PolicyNames for this tenant in authorizor-plugin
- // Typical Usage : find out all the bucket/user policies for a tenant.
- private final Map<String, List<String>> inMemoryTenantToPolicyNameListMap;
-
- // This Mapping maintains all groups for all tenants
- // key = tenantName
- // value = list of all GroupNames that belong to this tenant
- // There are at least two default groups created for every tenant.
- // Tenant_XYZ$GroupTenantAllUsers
- // Tenant_XYZ$GroupTenantAdmins
- // There are also predefined global groups like (TODO)
- // - AllAuthenticateUsers (TODO)
- // - AllUsers (TODO)
- // Typical usage : Put together all the users that have access to some
- // resource in the same group. E.g.
- // 1) users in Tenant_XYZ$GroupTenantAllUsers would be able to
- // access the volume created for Tenant_XYZ.
- // 2) If user creates an access policy for a bucket, all the users
- // that would have same access to the bucket can go in the same group.
- private final Map<String, List<String>> inMemoryTenantToTenantGroups;
-
- // Mapping for user-access-id to TenantName
- // Typical usage: given a user-access-id find out which tenant
- private final Map<String, String> inMemoryAccessIDToTenantNameMap;
-
- // Mapping from user-access-id to all the groups that they belong to.
- // Typical usage: Adding a user or modify user, provide a list of groups
- // that they would belong to. Note that groupIDs are opaque to OM.
- // This may make sense just to the authorizer-plugin.
- private final Map<String, List<String>> inMemoryAccessIDToListOfGroupsMap;
-
- // Used for testing (where there's no ranger instance) to inject a mock
- // authorizer. Use the normal Ranger plugin by default.
- private static Supplier<MultiTenantAccessAuthorizer> authorizerSupplier =
- MultiTenantAccessAuthorizerRangerPlugin::new;
-
+ private final Map<String, CachedTenantInfo> tenantCache;
OMMultiTenantManagerImpl(OMMetadataManager mgr, OzoneConfiguration conf)
throws IOException {
this.conf = conf;
- inMemoryTenantNameToTenantInfoMap = new ConcurrentHashMap<>();
- inMemoryTenantToPolicyNameListMap = new ConcurrentHashMap<>();
- inMemoryTenantToTenantGroups = new ConcurrentHashMap<>();
- inMemoryAccessIDToTenantNameMap = new ConcurrentHashMap<>();
- inMemoryAccessIDToListOfGroupsMap = new ConcurrentHashMap<>();
-
controlPathLock = new ReentrantReadWriteLock();
omMetadataManager = mgr;
-
- devSkipRanger = conf.getBoolean(OZONE_OM_TENANT_DEV_SKIP_RANGER, false);
- start(conf);
- }
-
- @VisibleForTesting
- public static void setAuthorizerSupplier(
- Supplier<MultiTenantAccessAuthorizer> authSupplier) {
- authorizerSupplier = authSupplier;
- }
-
- @Override
- public void start(OzoneConfiguration configuration) throws IOException {
+ tenantCache = new ConcurrentHashMap<>();
+ boolean devSkipRanger = conf.getBoolean(OZONE_OM_TENANT_DEV_SKIP_RANGER,
+ false);
if (devSkipRanger) {
authorizer = new MultiTenantAccessAuthorizerDummyPlugin();
} else {
authorizer = new MultiTenantAccessAuthorizerRangerPlugin();
}
- authorizer.init(configuration);
+ authorizer.init(conf);
+ loadUsersFromDB();
}
- @Override
- public void stop() throws Exception {
-
- }
+// start() and stop() lifeycle methods can be added when there is a background
+// work going on.
+// @Override
+// public void start() throws IOException {
+// }
+//
+// @Override
+// public void stop() throws Exception {
+//
+// }
@Override
public OMMetadataManager getOmMetadataManager() {
return omMetadataManager;
}
+ // TODO: Cleanup up this Java doc.
/**
* Algorithm
* OM State :
@@ -204,18 +158,12 @@ public class OMMultiTenantManagerImpl implements
OMMultiTenantManager {
* @throws IOException
*/
@Override
- public Tenant createTenant(String tenantID) throws IOException {
+ public Tenant createTenantAccessInAuthorizer(String tenantID)
+ throws IOException {
- Tenant tenant = new CephCompatibleTenantImpl(tenantID);
+ Tenant tenant = new DefaultOzoneS3Tenant(tenantID);
try {
controlPathLock.writeLock().lock();
- inMemoryTenantNameToTenantInfoMap.put(tenantID, tenant);
-
- // TODO : for now just create state in the Ranger. OM state is already
- // created in ValidateAndUpdateCache for the ratis transaction.
-
- // TODO : Make it an idempotent operation. If any ranger state creation
- // fails because it already exists, Ignore it.
// Create admin role first
final OzoneTenantRolePrincipal adminRole =
@@ -229,10 +177,6 @@ public class OMMultiTenantManagerImpl implements
OMMultiTenantManager {
String userRoleId = authorizer.createRole(userRole, adminRole.getName());
tenant.addTenantAccessRole(userRoleId);
- final List<String> allTenantRole =
- Arrays.asList(userRole.getName(), adminRole.getName());
- inMemoryTenantToTenantGroups.put(tenantID, allTenantRole);
-
BucketNameSpace bucketNameSpace = tenant.getTenantBucketNameSpace();
// bucket namespace is volume name ??
for (OzoneObj volume : bucketNameSpace.getBucketNameSpaceObjects()) {
@@ -253,35 +197,24 @@ public class OMMultiTenantManagerImpl implements
OMMultiTenantManager {
tenant.addTenantAccessPolicy(tenantBucketCreatePolicy);
}
- inMemoryTenantToPolicyNameListMap.put(tenantID,
- tenant.getTenantAccessPolicies().stream().map(
- AccessPolicy::getPolicyName).collect(Collectors.toList()));
+ tenantCache.put(tenantID, new CachedTenantInfo(tenantID));
} catch (Exception e) {
try {
- destroyTenant(tenant);
+ removeTenantAccessFromAuthorizer(tenant);
} catch (Exception exception) {
// Best effort cleanup.
}
- controlPathLock.writeLock().unlock();
throw new IOException(e.getMessage());
+ } finally {
+ controlPathLock.writeLock().unlock();
}
- controlPathLock.writeLock().unlock();
return tenant;
}
@Override
public Tenant getTenantInfo(String tenantID) throws IOException {
- // TODO:Should read from DB. Ditch the in-memory maps.
- if (!inMemoryTenantNameToTenantInfoMap.containsKey(tenantID)) {
- return null;
- }
- for (Map.Entry<String, Tenant> entry :
- inMemoryTenantNameToTenantInfoMap.entrySet()) {
- if (entry.getKey().equals(tenantID)) {
- return entry.getValue();
- }
- }
- throw new IOException("All Tenants Map is corrupt");
+ // Todo : fix this.
+ return null;
}
@Override
@@ -290,9 +223,7 @@ public class OMMultiTenantManagerImpl implements
OMMultiTenantManager {
}
@Override
- public void destroyTenant(Tenant tenant) throws Exception {
- // TODO: Make sure this is idempotent. This can be called by ALL 3 OMs
- // in the case of a createTenant checkAcl failure for instance.
+ public void removeTenantAccessFromAuthorizer(Tenant tenant) throws Exception
{
try {
controlPathLock.writeLock().lock();
for (AccessPolicy policy : tenant.getTenantAccessPolicies()) {
@@ -301,29 +232,22 @@ public class OMMultiTenantManagerImpl implements
OMMultiTenantManager {
for (String roleId : tenant.getTenantRoles()) {
authorizer.deleteRole(roleId);
}
-
- inMemoryTenantNameToTenantInfoMap.remove(tenant.getTenantId());
- inMemoryTenantToPolicyNameListMap.remove(tenant.getTenantId());
- inMemoryTenantToTenantGroups.remove(tenant.getTenantId());
- } catch (Exception e) {
+ if (tenantCache.containsKey(tenant.getTenantId())) {
+ LOG.info("Removing tenant {} from in memory cached state",
+ tenant.getTenantId());
+ tenantCache.remove(tenant.getTenantId());
+ }
+ } finally {
controlPathLock.writeLock().unlock();
- throw e;
}
- controlPathLock.writeLock().unlock();
}
/**
* Algorithm
- * OM State :
- * - Validation (Part of Ratis Request)
- * - create user in OMDB {Part of RATIS request}
- * - Persistence to OM DB {Part of RATIS request}
* Authorizer-plugin(Ranger) State :
* - create User in Ranger DB
* - For every user created
* Add them to # GroupTenantAllUsers
- * Finally :
- * - Update all Maps maintained by Multi-Tenant-Manager
* In case of failure :
* - Undo all Ranger State
* - remove updates to the Map
@@ -340,27 +264,29 @@ public class OMMultiTenantManagerImpl implements
OMMultiTenantManager {
*/
@Override
public String assignUserToTenant(BasicUserPrincipal principal,
- String tenantName, String accessID) throws IOException {
+ String tenantName,
+ String accessID) throws IOException {
+ ImmutablePair<String, String> userAccessIdPair =
+ new ImmutablePair<>(principal.getName(), accessID);
try {
controlPathLock.writeLock().lock();
- Tenant tenant = getTenantInfo(tenantName);
- if (tenant == null) {
- LOG.error("Cannot assign user to tenant {} that doesn't exist",
- tenantName);
- return null;
- }
+
+ LOG.info("Adding user '{}' to tenant '{}' in-memory state.",
+ principal.getName(), tenantName);
+ CachedTenantInfo cachedTenantInfo =
+ tenantCache.getOrDefault(tenantName,
+ new CachedTenantInfo(tenantName));
+ cachedTenantInfo.getTenantUsers().add(userAccessIdPair);
+
final OzoneTenantRolePrincipal roleTenantAllUsers =
OzoneTenantRolePrincipal.getUserRole(tenantName);
String roleJsonStr = authorizer.getRole(roleTenantAllUsers);
String roleId = authorizer.assignUser(principal, roleJsonStr, false);
-
- inMemoryAccessIDToTenantNameMap.put(accessID, tenantName);
-// inMemoryAccessIDToListOfGroupsMap.put(accessID, userRoleIds);
-
return roleId;
- } catch (IOException e) {
+ } catch (Exception e) {
revokeUserAccessId(accessID);
- throw e;
+ tenantCache.get(tenantName).getTenantUsers().remove(userAccessIdPair);
+ throw new OMException(e.getMessage(), TENANT_AUTHORIZER_ERROR);
} finally {
controlPathLock.writeLock().unlock();
}
@@ -370,22 +296,29 @@ public class OMMultiTenantManagerImpl implements
OMMultiTenantManager {
public void revokeUserAccessId(String accessID) throws IOException {
try {
controlPathLock.writeLock().lock();
- String tenantName = getTenantForAccessID(accessID);
+ OmDBAccessIdInfo omDBAccessIdInfo =
+ omMetadataManager.getTenantAccessIdTable().get(accessID);
+ if (omDBAccessIdInfo == null) {
+ throw new OMException(INVALID_ACCESSID);
+ }
+ String tenantName = omDBAccessIdInfo.getTenantId();
if (tenantName == null) {
LOG.error("Tenant doesn't exist");
return;
}
+ tenantCache.get(tenantName).getTenantUsers()
+ .remove(new ImmutablePair<>(omDBAccessIdInfo.getKerberosPrincipal(),
+ accessID));
// TODO: Determine how to replace this code.
// final String userID = authorizer.getUserId(userPrincipal);
// authorizer.deleteUser(userID);
- inMemoryAccessIDToTenantNameMap.remove(accessID);
- inMemoryAccessIDToListOfGroupsMap.remove(accessID);
} finally {
controlPathLock.writeLock().unlock();
}
}
+
@Override
public String getUserNameGivenAccessId(String accessId) {
Preconditions.checkNotNull(accessId);
@@ -427,16 +360,53 @@ public class OMMultiTenantManagerImpl implements
OMMultiTenantManager {
}
@Override
- public List<String> listAllAccessIDs(String tenantID)
+ public boolean isTenantAdmin(String user, String tenantName) {
+ return true;
+ }
+
+ @Override
+ public TenantUserList listUsersInTenant(String tenantID, String prefix)
throws IOException {
- return null;
+
+ if (!omMetadataManager.getTenantStateTable().isExist(tenantID)) {
+ throw new IOException("Tenant '" + tenantID + "' not found!");
+ }
+
+ List<TenantUserAccessId> userAccessIds = new ArrayList<>();
+ CachedTenantInfo cachedTenantInfo = tenantCache.get(tenantID);
+ if (cachedTenantInfo == null) {
+ throw new IOException("Inconsistent in memory Tenant cache '" + tenantID
+ + "' not found in cache, but present in OM DB!");
+ }
+
+ cachedTenantInfo.getTenantUsers().stream()
+ .filter(
+ k -> StringUtils.isEmpty(prefix) || k.getKey().startsWith(prefix))
+ .forEach(
+ k -> userAccessIds.add(
+ TenantUserAccessId.newBuilder()
+ .setUser(k.getKey())
+ .setAccessId(k.getValue())
+ .build()));
+ return new TenantUserList(tenantID, userAccessIds);
}
@Override
- public String getTenantForAccessID(String accessID) {
- return inMemoryAccessIDToTenantNameMap.getOrDefault(accessID, null);
+ public String getTenantForAccessID(String accessID) throws IOException {
+ OmDBAccessIdInfo omDBAccessIdInfo =
+ omMetadataManager.getTenantAccessIdTable().get(accessID);
+ if (omDBAccessIdInfo == null) {
+ throw new OMException(INVALID_ACCESSID);
+ }
+ return omDBAccessIdInfo.getTenantId();
}
+ public List<String> listAllAccessIDs(String tenantID)
+ throws IOException {
+ return null;
+ }
+
+
@Override
public void assignTenantAdmin(String accessID, boolean delegated)
throws IOException {
@@ -615,4 +585,37 @@ public class OMMultiTenantManagerImpl implements
OMMultiTenantManager {
public OzoneConfiguration getConf() {
return conf;
}
+
+ public void loadUsersFromDB() {
+ Table<String, OmDBAccessIdInfo> tenantAccessIdTable =
+ omMetadataManager.getTenantAccessIdTable();
+ TableIterator<String, ? extends KeyValue<String, OmDBAccessIdInfo>>
+ iterator = tenantAccessIdTable.iterator();
+ int userCount = 0;
+
+ try {
+ while (iterator.hasNext()) {
+ KeyValue<String, OmDBAccessIdInfo> next = iterator.next();
+ String accessId = next.getKey();
+ OmDBAccessIdInfo value = next.getValue();
+ String tenantId = value.getTenantId();
+ String user = value.getKerberosPrincipal();
+
+ CachedTenantInfo cachedTenantInfo = tenantCache
+ .computeIfAbsent(tenantId, k -> new CachedTenantInfo(tenantId));
+ cachedTenantInfo.getTenantUsers().add(
+ new ImmutablePair<>(user, accessId));
+ userCount++;
+ }
+ LOG.info("Loaded {} tenants and {} tenant-users from the database.",
+ tenantCache.size(), userCount);
+ } catch (Exception ex) {
+ LOG.error("Error while loading user list. ", ex);
+ }
+ }
+
+ @VisibleForTesting
+ Map<String, CachedTenantInfo> getTenantCache() {
+ return tenantCache;
+ }
}
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 114bb20..4b59387 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
@@ -140,6 +140,7 @@ import org.apache.hadoop.ozone.om.helpers.ServiceInfo;
import org.apache.hadoop.ozone.om.helpers.ServiceInfoEx;
import org.apache.hadoop.ozone.om.helpers.TenantInfoList;
import org.apache.hadoop.ozone.om.helpers.TenantUserInfoValue;
+import org.apache.hadoop.ozone.om.helpers.TenantUserList;
import org.apache.hadoop.ozone.om.protocol.OMInterServiceProtocol;
import
org.apache.hadoop.ozone.om.protocolPB.OMInterServiceProtocolClientSideImpl;
import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol;
@@ -255,6 +256,7 @@ import static
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_VOLUME_LISTALL_AL
import static
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.DETECTED_LOOP_IN_BUCKET_LINKS;
import static
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.INVALID_AUTH_METHOD;
import static
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.INVALID_REQUEST;
+import static
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.INVALID_ACCESSID;
import static
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.KEY_NOT_FOUND;
import static
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.PERMISSION_DENIED;
import static
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.TOKEN_ERROR_OTHER;
@@ -3213,27 +3215,64 @@ public final class OzoneManager extends
ServiceRuntimeInfoImpl
}
@Override
- public OmVolumeArgs getS3Volume(String accessID) throws IOException {
- String tenantName = multiTenantManagr.getTenantForAccessID(accessID);
- if (tenantName == null) {
- // If the user is not associated with a tenant, they will use the
- // default s3 volume.
- String defaultS3volume =
- HddsClientUtils.getDefaultS3VolumeName(configuration);
+ public TenantUserList listUsersInTenant(String tenantName, String prefix)
+ throws IOException {
- if (LOG.isDebugEnabled()) {
- LOG.debug("No tenant found for access ID {}. Directing " +
- "requests to default s3 volume {}.", accessID, defaultS3volume);
+ if (StringUtils.isEmpty(tenantName)) {
+ return null;
+ }
+
+ final Map<String, String> auditMap = new LinkedHashMap<>();
+ auditMap.put(OzoneConsts.TENANT, tenantName);
+ auditMap.put(OzoneConsts.USER_PREFIX, prefix);
+ try {
+ String userName = getRemoteUser().getUserName();
+ if (!multiTenantManagr.isTenantAdmin(userName, tenantName)
+ && !omAdminUsernames.contains(userName)) {
+ throw new IOException("Only tenant and ozone admins can access this " +
+ "API. '" + userName + "' is not an admin.");
}
- return getVolumeInfo(defaultS3volume);
- } else {
+
+ final TenantUserList userList =
+ multiTenantManagr.listUsersInTenant(tenantName, prefix);
+ AUDIT.logReadSuccess(buildAuditMessageForSuccess(
+ OMAction.TENANT_LIST_USER, auditMap));
+ return userList;
+ } catch (IOException ex) {
+ AUDIT.logReadFailure(buildAuditMessageForFailure(
+ OMAction.TENANT_LIST_USER, auditMap, ex));
+ throw ex;
+ }
+ }
+
+ @Override
+ public OmVolumeArgs getS3Volume(String accessID) throws IOException {
+
+ String tenantName;
+ try {
+ tenantName = multiTenantManagr.getTenantForAccessID(accessID);
if (LOG.isDebugEnabled()) {
LOG.debug("Get S3 volume request for access ID {} belonging to tenant"
+
- " {} is directed to the volume {}.", accessID, tenantName,
+ " {} is directed to the volume {}.", accessID, tenantName,
tenantName);
}
// This call performs acl checks and checks volume existence.
return getVolumeInfo(tenantName);
+
+ } catch (OMException ex) {
+ if (ex.getResult().equals(INVALID_ACCESSID)) {
+ // If the user is not associated with a tenant, they will use the
+ // default s3 volume.
+ String defaultS3volume =
+ HddsClientUtils.getDefaultS3VolumeName(configuration);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("No tenant found for access ID {}. Directing " +
+ "requests to default s3 volume {}.", accessID, defaultS3volume);
+ }
+ return getVolumeInfo(defaultS3volume);
+ }
+ throw ex;
}
}
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/multitenant/CachedTenantInfo.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/multitenant/CachedTenantInfo.java
new file mode 100644
index 0000000..b93eea2
--- /dev/null
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/multitenant/CachedTenantInfo.java
@@ -0,0 +1,45 @@
+/**
+ * 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.om.multitenant;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.commons.lang3.tuple.Pair;
+
+/**
+ * A collection of things that we want to maintain about a tenant in memory.
+ */
+public class CachedTenantInfo {
+
+ private String tenantId;
+ private Set<Pair<String, String>> tenantUserAccessIds;
+
+ public CachedTenantInfo(String tenantId) {
+ this.tenantId = tenantId;
+ tenantUserAccessIds = new HashSet<>();
+ }
+
+ public Set<Pair<String, String>> getTenantUsers() {
+ return tenantUserAccessIds;
+ }
+
+ public String getTenantId() {
+ return tenantId;
+ }
+}
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenantImpl/package-info.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/multitenant/package-info.java
similarity index 73%
rename from
hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenantImpl/package-info.java
rename to
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/multitenant/package-info.java
index 0b46716..5be4f96 100644
---
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenantImpl/package-info.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/multitenant/package-info.java
@@ -1,23 +1,23 @@
-/*
+/**
* 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
+ * with the License. You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * 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.multitenant;
+
/**
- * Package contains classes related to Ozone multi-tenant implementation.
+ * This package contains classes related to Ozone Multi tenancy.
*/
-package org.apache.hadoop.ozone.om.multitenantImpl;
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMAssignUserToTenantRequest.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMAssignUserToTenantRequest.java
index 9b4a747..a2af9ca 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMAssignUserToTenantRequest.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMAssignUserToTenantRequest.java
@@ -219,7 +219,8 @@ public class OMAssignUserToTenantRequest extends
OMClientRequest {
final TenantAssignUserAccessIdRequest request =
getOmRequest().getTenantAssignUserAccessIdRequest();
final String tenantName = request.getTenantName();
- final String principal = request.getTenantUsername(); // TODO: Rename this
+ final String principal = request.getTenantUsername();
+
assert(accessId.equals(request.getAccessId()));
IOException exception = null;
@@ -341,7 +342,6 @@ public class OMAssignUserToTenantRequest extends
OMClientRequest {
omResponse.build(), s3SecretValue, principal, defaultGroupName,
roleName, accessId, omDBAccessIdInfo, principalInfo);
} catch (IOException ex) {
- // Error handling
handleRequestFailure(ozoneManager);
exception = ex;
// Set response success flag to false
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantCreateRequest.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantCreateRequest.java
index 49f329c..d1c8283 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantCreateRequest.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantCreateRequest.java
@@ -102,6 +102,8 @@ public class OMTenantCreateRequest extends OMVolumeRequest {
public static final Logger LOG =
LoggerFactory.getLogger(OMTenantCreateRequest.class);
+ private transient Tenant tenantInContext;
+
public OMTenantCreateRequest(OMRequest omRequest) {
super(omRequest);
}
@@ -159,11 +161,12 @@ public class OMTenantCreateRequest extends
OMVolumeRequest {
// If we fail after pre-execute. handleRequestFailure() callback
// would clean up any state maintained by the getMultiTenantManager.
- final Tenant tenant =
- ozoneManager.getMultiTenantManager().createTenant(tenantName);
+ tenantInContext = ozoneManager.getMultiTenantManager()
+ .createTenantAccessInAuthorizer(tenantName);
// Get the tenant default policy, pass this along
- final String tenantDefaultPolicies = tenant.getTenantAccessPolicies()
+ final String tenantDefaultPolicies = tenantInContext
+ .getTenantAccessPolicies()
.stream().map(AccessPolicy::getPolicyID)
.collect(Collectors.joining(","));
@@ -187,14 +190,11 @@ public class OMTenantCreateRequest extends
OMVolumeRequest {
@Override
public void handleRequestFailure(OzoneManager ozoneManager) {
- final CreateTenantRequest request =
getOmRequest().getCreateTenantRequest();
-
try {
- final Tenant tenant = ozoneManager.getMultiTenantManager()
- .getTenantInfo(request.getTenantName());
// Cleanup any state maintained by OMMultiTenantManager
- if (tenant != null) {
- ozoneManager.getMultiTenantManager().destroyTenant(tenant);
+ if (tenantInContext != null) {
+ ozoneManager.getMultiTenantManager()
+ .removeTenantAccessFromAuthorizer(tenantInContext);
}
} catch (Exception e) {
// TODO: Ignore for now. Multi-Tenant Manager is responsible for
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java
index 532b268..07b2b89 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java
@@ -45,6 +45,7 @@ import org.apache.hadoop.ozone.om.helpers.ServiceInfo;
import org.apache.hadoop.ozone.om.helpers.ServiceInfoEx;
import org.apache.hadoop.ozone.om.helpers.TenantInfoList;
import org.apache.hadoop.ozone.om.helpers.TenantUserInfoValue;
+import org.apache.hadoop.ozone.om.helpers.TenantUserList;
import org.apache.hadoop.ozone.om.ratis.OzoneManagerDoubleBuffer;
import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerRatisUtils;
import org.apache.hadoop.ozone.om.request.OMClientRequest;
@@ -88,6 +89,8 @@ import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetS3Vo
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Status;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantGetUserInfoRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantGetUserInfoResponse;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantListUserRequest;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantListUserResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type;
import org.apache.hadoop.ozone.security.acl.OzoneObjInfo;
@@ -244,6 +247,11 @@ public class OzoneManagerRequestHandler implements
RequestHandler {
request.getListTenantRequest());
responseBuilder.setListTenantResponse(listTenantResponse);
break;
+ case TenantListUser:
+ TenantListUserResponse listUserResponse = tenantListUsers(
+ request.getTenantListUserRequest());
+ responseBuilder.setTenantListUserResponse(listUserResponse);
+ break;
default:
responseBuilder.setSuccess(false);
responseBuilder.setMessage("Unrecognized Command Type: " + cmdType);
@@ -386,6 +394,22 @@ public class OzoneManagerRequestHandler implements
RequestHandler {
return resp.build();
}
+ private TenantListUserResponse tenantListUsers(
+ TenantListUserRequest request) throws IOException {
+ TenantListUserResponse.Builder builder =
+ TenantListUserResponse.newBuilder();
+ TenantUserList usersInTenant =
+ impl.listUsersInTenant(request.getTenantName(), request.getPrefix());
+ if (usersInTenant == null) {
+ builder.setSuccess(false);
+ } else {
+ builder.setSuccess(true);
+ builder.setTenantName(request.getTenantName());
+ builder.addAllUserAccessIdInfo(usersInTenant.getUserAccessIds());
+ }
+ return builder.build();
+ }
+
private ListTenantResponse listTenant(
ListTenantRequest request) throws IOException {
diff --git
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOMMultiTenantManagerImpl.java
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOMMultiTenantManagerImpl.java
new file mode 100644
index 0000000..de87581
--- /dev/null
+++
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOMMultiTenantManagerImpl.java
@@ -0,0 +1,138 @@
+/*
+ * 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.ozone.om.OMConfigKeys.OZONE_OM_DB_DIRS;
+import static
org.apache.hadoop.ozone.om.OMMultiTenantManagerImpl.OZONE_OM_TENANT_DEV_SKIP_RANGER;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.ozone.OzoneConsts;
+import org.apache.hadoop.ozone.om.exceptions.OMException;
+import org.apache.hadoop.ozone.om.helpers.OmDBAccessIdInfo;
+import org.apache.hadoop.ozone.om.helpers.OmDBTenantInfo;
+import org.apache.hadoop.ozone.om.helpers.TenantUserList;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantUserAccessId;
+import org.apache.http.auth.BasicUserPrincipal;
+import org.apache.ozone.test.LambdaTestUtils;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+/**
+ * Tests for Multi Tenant Manager APIs.
+ */
+public class TestOMMultiTenantManagerImpl {
+
+ private OMMultiTenantManagerImpl tenantManager;
+ private static String tenantName = "tenant1";
+
+ @Rule
+ public TemporaryFolder folder = new TemporaryFolder();
+
+ @Before
+ public void setUp() throws IOException {
+ OzoneConfiguration conf = new OzoneConfiguration();
+ conf.set(OZONE_OM_DB_DIRS,
+ folder.newFolder().getAbsolutePath());
+ conf.set(OZONE_OM_TENANT_DEV_SKIP_RANGER, "true");
+ OMMetadataManager omMetadataManager = new OmMetadataManagerImpl(conf);
+
+ final String bucketNamespaceName = tenantName;
+ final String accountNamespaceName = tenantName;
+ final String userPolicyGroupName =
+ tenantName + OzoneConsts.DEFAULT_TENANT_USER_POLICY_SUFFIX;
+ final String bucketPolicyGroupName =
+ tenantName + OzoneConsts.DEFAULT_TENANT_BUCKET_POLICY_SUFFIX;
+
+ final OmDBTenantInfo omDBTenantInfo = new OmDBTenantInfo(
+ tenantName, bucketNamespaceName, accountNamespaceName,
+ userPolicyGroupName, bucketPolicyGroupName);
+ omMetadataManager.getTenantStateTable().put(tenantName, omDBTenantInfo);
+
+ omMetadataManager.getTenantAccessIdTable().put("seed-accessId1",
+ new OmDBAccessIdInfo(tenantName, "seed-user1",
+ "sharedsecret1", false, false));
+
+ tenantManager = new OMMultiTenantManagerImpl(omMetadataManager, conf);
+ assertEquals(1, tenantManager.getTenantCache().size());
+ assertEquals(1,
+
tenantManager.getTenantCache().get(tenantName).getTenantUsers().size());
+
+ }
+
+ @Test
+ public void testListUsersInTenant() throws Exception {
+ tenantManager.assignUserToTenant(new BasicUserPrincipal("user1"),
+ tenantName, "accessId1");
+
+ TenantUserList tenantUserList =
+ tenantManager.listUsersInTenant(tenantName, "");
+ List<TenantUserAccessId> userAccessIds = tenantUserList.getUserAccessIds();
+ assertEquals(2, userAccessIds.size());
+
+ for (TenantUserAccessId userAccessId : userAccessIds) {
+ String user = userAccessId.getUser();
+ if (user.equals("user1")) {
+ assertEquals("accessId1", userAccessId.getAccessId());
+ } else if (user.equals("seed-user1")) {
+ assertEquals("seed-accessId1", userAccessId.getAccessId());
+ } else {
+ Assert.fail();
+ }
+ }
+
+ LambdaTestUtils.intercept(IOException.class,
+ "Tenant 'tenant2' not found", () -> {
+ tenantManager.listUsersInTenant("tenant2", null);
+ });
+
+ assertTrue(tenantManager.listUsersInTenant(tenantName, "abc")
+ .getUserAccessIds().isEmpty());
+ }
+
+ @Test
+ public void testRevokeUserAccessId() throws Exception {
+
+ LambdaTestUtils.intercept(OMException.class, () ->
+ tenantManager.revokeUserAccessId("accessId1"));
+ assertEquals(1, tenantManager.getTenantCache().size());
+
+ tenantManager.revokeUserAccessId("seed-accessId1");
+ assertTrue(tenantManager.getTenantCache()
+ .get(tenantName).getTenantUsers().isEmpty());
+ assertTrue(tenantManager.listUsersInTenant(tenantName, null)
+ .getUserAccessIds().isEmpty());
+ }
+
+ @Test
+ public void testGetTenantForAccessID() throws Exception {
+ assertEquals(tenantName, tenantManager.getTenantForAccessID("seed" +
+ "-accessId1"));
+ LambdaTestUtils.intercept(OMException.class, () -> {
+ tenantManager.getTenantForAccessID("invalid-accessId1");
+ });
+ }
+}
\ No newline at end of file
diff --git
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMKeyAclRequestWithFSO.java
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMKeyAclRequestWithFSO.java
index 2870bb0..42906fa 100644
---
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMKeyAclRequestWithFSO.java
+++
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMKeyAclRequestWithFSO.java
@@ -24,7 +24,7 @@ import
org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerRatisUtils;
import org.apache.hadoop.ozone.om.request.TestOMRequestUtils;
import org.apache.hadoop.ozone.om.request.key.acl.OMKeyAclRequest;
import org.apache.hadoop.ozone.om.request.key.acl.OMKeyAddAclRequestWithFSO;
-import
org.apache.hadoop.ozone.om.request.key.acl.OMKeyRemoveAclRequestWithFSO;;
+import org.apache.hadoop.ozone.om.request.key.acl.OMKeyRemoveAclRequestWithFSO;
import org.apache.hadoop.ozone.om.request.key.acl.OMKeySetAclRequestWithFSO;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
import org.apache.hadoop.util.Time;
diff --git
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/s3/security/TestS3GetSecretRequest.java
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/s3/security/TestS3GetSecretRequest.java
index 4a47251..cdc1132 100644
---
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/s3/security/TestS3GetSecretRequest.java
+++
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/s3/security/TestS3GetSecretRequest.java
@@ -133,11 +133,11 @@ public class TestS3GetSecretRequest {
// Multi-tenant related initializations
omMultiTenantManager = mock(OMMultiTenantManager.class);
tenant = mock(Tenant.class);
- when(omMultiTenantManager.getTenantInfo(TENANT_NAME)).thenReturn(tenant);
when(ozoneManager.getMultiTenantManager()).thenReturn(omMultiTenantManager);
when(tenant.getTenantAccessPolicies()).thenReturn(new ArrayList<>());
- when(omMultiTenantManager.createTenant(TENANT_NAME)).thenReturn(tenant);
+ when(omMultiTenantManager.createTenantAccessInAuthorizer(TENANT_NAME))
+ .thenReturn(tenant);
}
@After
diff --git
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantListUsersHandler.java
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantListUsersHandler.java
new file mode 100644
index 0000000..458a458
--- /dev/null
+++
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantListUsersHandler.java
@@ -0,0 +1,71 @@
+/*
+ * 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.shell.tenant;
+
+import java.io.IOException;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.ozone.client.ObjectStore;
+import org.apache.hadoop.ozone.client.OzoneClient;
+import org.apache.hadoop.ozone.om.helpers.TenantUserList;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantUserAccessId;
+import org.apache.hadoop.ozone.shell.OzoneAddress;
+import org.apache.hadoop.ozone.shell.s3.S3Handler;
+
+import picocli.CommandLine;
+
+/**
+ * Command to list users in a tenant along with corresponding accessId.
+ */
[email protected](name = "list",
+ description = "List Tenant Users")
+public class TenantListUsersHandler extends S3Handler {
+
+ @CommandLine.Spec
+ private CommandLine.Model.CommandSpec spec;
+
+ @CommandLine.Option(names = {"-t", "--tenant"},
+ description = "Tenant name")
+ private String tenantName;
+
+ @CommandLine.Option(names = {"-p", "--prefix"},
+ description = "Filter users with this prefix.")
+ private String prefix;
+
+ @Override
+ protected void execute(OzoneClient client, OzoneAddress address) {
+ final ObjectStore objStore = client.getObjectStore();
+
+ if (StringUtils.isEmpty(tenantName)) {
+ err().println("Please specify a tenant name with -t.");
+ return;
+ }
+ try {
+ TenantUserList usersInTenant =
+ objStore.listUsersInTenant(tenantName, prefix);
+ for (TenantUserAccessId accessIdInfo : usersInTenant.getUserAccessIds())
{
+ out().println("- User '" + accessIdInfo.getUser() +
+ "' with accessId '" + accessIdInfo.getAccessId() + "'");
+ }
+ } catch (IOException e) {
+ err().println("Failed to Get Users in tenant '" + tenantName
+ + "': " + e.getMessage());
+ }
+ }
+}
diff --git
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantUserCommands.java
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantUserCommands.java
index 591d3b0..356fafa 100644
---
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantUserCommands.java
+++
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantUserCommands.java
@@ -39,7 +39,8 @@ import java.util.concurrent.Callable;
TenantAssignUserAccessIdHandler.class,
TenantRevokeUserAccessIdHandler.class,
TenantAssignAdminHandler.class,
- TenantRevokeAdminHandler.class
+ TenantRevokeAdminHandler.class,
+ TenantListUsersHandler.class
},
mixinStandardHelpOptions = true,
versionProvider = HddsVersionProvider.class)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]