You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ozone.apache.org by si...@apache.org on 2021/10/28 22:13:46 UTC
[ozone] branch HDDS-4944 updated: HDDS-5776. [Multi-Tenant]
Implement AssignTenantAdmin, RevokeTenantAdmin, ListTenant,
RevokeAccessID (#2734)
This is an automated email from the ASF dual-hosted git repository.
siyao 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 8ecff6f HDDS-5776. [Multi-Tenant] Implement AssignTenantAdmin, RevokeTenantAdmin, ListTenant, RevokeAccessID (#2734)
8ecff6f is described below
commit 8ecff6fd1a5842e8c81c125fd1d5aa729b7c7a47
Author: Siyao Meng <50...@users.noreply.github.com>
AuthorDate: Thu Oct 28 15:13:24 2021 -0700
HDDS-5776. [Multi-Tenant] Implement AssignTenantAdmin, RevokeTenantAdmin, ListTenant, RevokeAccessID (#2734)
---
.../java/org/apache/hadoop/ozone/OzoneConsts.java | 17 +-
.../apache/hadoop/ozone/client/ObjectStore.java | 53 ++++-
.../ozone/client/protocol/ClientProtocol.java | 35 ++-
.../apache/hadoop/ozone/client/rpc/RpcClient.java | 61 ++++-
.../main/java/org/apache/hadoop/ozone/OmUtils.java | 7 +-
.../org/apache/hadoop/ozone/audit/OMAction.java | 8 +-
.../hadoop/ozone/om/exceptions/OMException.java | 7 +-
.../hadoop/ozone/om/helpers/OmDBAccessIdInfo.java | 60 ++++-
.../om/helpers/OmDBKerberosPrincipalInfo.java | 6 +-
.../hadoop/ozone/om/helpers/TenantInfoList.java | 71 ++++++
.../hadoop/ozone/om/lock/OzoneManagerLock.java | 1 +
.../om/multitenant/CephCompatibleTenantImpl.java | 18 +-
.../multitenant/MultiTenantAccessAuthorizer.java | 35 +--
.../MultiTenantAccessAuthorizerDummyPlugin.java | 16 +-
.../MultiTenantAccessAuthorizerRangerPlugin.java | 196 ++++++++--------
.../ozone/om/multitenant/OzoneOwnerPrincipal.java} | 24 +-
...rincipal.java => OzoneTenantRolePrincipal.java} | 20 +-
.../ozone/om/multitenant/RangerAccessPolicy.java | 4 +-
.../apache/hadoop/ozone/om/multitenant/Tenant.java | 6 +-
.../ozone/om/protocol/OzoneManagerProtocol.java | 35 ++-
...OzoneManagerProtocolClientSideTranslatorPB.java | 114 +++++++++-
.../ozonesecure/mockserverInitialization.json | 20 +-
.../smoketest/security/ozone-secure-tenant.robot | 17 +-
...estMultiTenantAccessAuthorizerRangerPlugin.java | 63 +++---
.../om/multitenant/TestMultiTenantVolume.java | 2 +-
.../hadoop/ozone/shell/TestOzoneTenantShell.java | 213 ++++++++++++++---
.../src/main/proto/OmClientProtocol.proto | 115 +++++++---
.../apache/hadoop/ozone/om/OMMetadataManager.java | 2 -
.../hadoop/ozone/om/OMMultiTenantManager.java | 9 +-
.../hadoop/ozone/om/OMMultiTenantManagerImpl.java | 186 +++++++++------
.../hadoop/ozone/om/OmMetadataManagerImpl.java | 19 +-
.../org/apache/hadoop/ozone/om/OzoneManager.java | 150 +++++++++++-
.../hadoop/ozone/om/codec/OMDBDefinition.java | 14 +-
.../om/ratis/utils/OzoneManagerRatisUtils.java | 16 +-
.../s3/tenant/OMAssignUserToTenantRequest.java | 174 +++++++++-----
.../tenant/OMRevokeUserAccessToTenantRequest.java | 50 ----
.../s3/tenant/OMTenantAssignAdminRequest.java | 246 ++++++++++++++++++++
.../request/s3/tenant/OMTenantCreateRequest.java | 14 +-
.../request/s3/tenant/OMTenantRequestHelper.java | 143 ++++++++++++
.../s3/tenant/OMTenantRevokeAdminRequest.java | 221 ++++++++++++++++++
.../tenant/OMTenantRevokeUserAccessIdRequest.java | 251 +++++++++++++++++++++
.../s3/tenant/OMTenantAssignAdminResponse.java | 76 +++++++
...ava => OMTenantAssignUserAccessIdResponse.java} | 10 +-
.../s3/tenant/OMTenantRevokeAdminResponse.java | 76 +++++++
...ava => OMTenantRevokeUserAccessIdResponse.java} | 62 ++---
.../protocolPB/OzoneManagerRequestHandler.java | 21 ++
.../s3/security/TestS3GetSecretRequest.java | 26 ++-
.../ozone/shell/tenant/GetUserInfoHandler.java | 14 +-
.../shell/tenant/TenantAssignAdminHandler.java | 80 +++++++
...r.java => TenantAssignUserAccessIdHandler.java} | 36 +--
.../ozone/shell/tenant/TenantListHandler.java | 90 ++++++++
.../shell/tenant/TenantRevokeAdminHandler.java | 74 ++++++
...r.java => TenantRevokeUserAccessIdHandler.java} | 27 ++-
.../hadoop/ozone/shell/tenant/TenantShell.java | 1 +
.../ozone/shell/tenant/TenantUserCommands.java | 6 +-
55 files changed, 2683 insertions(+), 635 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 371885a..9eb96db 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
@@ -337,6 +337,7 @@ public final class OzoneConsts {
// For multi-tenancy
public static final String TENANT_NAME_USER_NAME_DELIMITER = "$";
+ public static final String TENANT_NAME_ROLE_DELIMITER = "-";
public static final String DEFAULT_TENANT_USER_POLICY_SUFFIX = "-users";
public static final String DEFAULT_TENANT_BUCKET_POLICY_SUFFIX = "-buckets";
public static final String DEFAULT_TENANT_POLICY_ID_SUFFIX = "-default";
@@ -458,18 +459,26 @@ public final class OzoneConsts {
public static final String OZONE_OM_RANGER_ADMIN_CREATE_USER_HTTP_ENDPOINT =
"/service/xusers/secure/users";
+ // Ideally we should use /addUsersAndGroups endpoint for add user to role,
+ // but it always return 405 somehow.
+ // https://ranger.apache.org/apidocs/resource_RoleREST.html
+ // #resource_RoleREST_addUsersAndGroups_PUT
+ public static final String OZONE_OM_RANGER_ADMIN_ROLE_ADD_USER_HTTP_ENDPOINT =
+ "/service/roles/roles/";
+
public static final String OZONE_OM_RANGER_ADMIN_GET_USER_HTTP_ENDPOINT =
"/service/xusers/users/?name=";
public static final String OZONE_OM_RANGER_ADMIN_DELETE_USER_HTTP_ENDPOINT =
"/service/xusers/secure/users/id/";
- public static final String OZONE_OM_RANGER_ADMIN_CREATE_GROUP_HTTP_ENDPOINT =
- "/service/xusers/secure/groups";
+ public static final String OZONE_OM_RANGER_ADMIN_CREATE_ROLE_HTTP_ENDPOINT =
+ "/service/roles/roles";
- public static final String OZONE_OM_RANGER_ADMIN_GET_GROUP_HTTP_ENDPOINT =
- "/service/xusers/groups?name=";
+ public static final String OZONE_OM_RANGER_ADMIN_GET_ROLE_HTTP_ENDPOINT =
+ "/service/roles/roles/name/";
+ // TODO: Change to delete role endpoint
public static final String OZONE_OM_RANGER_ADMIN_DELETE_GROUP_HTTP_ENDPOINT =
"/service/xusers/secure/groups/id/";
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 acc18b7..ba6f353 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
@@ -34,6 +34,7 @@ import org.apache.hadoop.ozone.OzoneAcl;
import org.apache.hadoop.ozone.client.protocol.ClientProtocol;
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.security.OzoneTokenIdentifier;
import org.apache.hadoop.ozone.security.acl.OzoneObj;
@@ -196,18 +197,49 @@ public class ObjectStore {
// proxy.createTenant(tenantName, tenantArgs);
// }
- // TODO: modify, delete
-
/**
- * Assign user to tenant.
+ * Assign user accessId to tenant.
* @param username user name to be assigned.
* @param tenantName tenant name.
- * @param accessId access ID.
+ * @param accessId Specified accessId.
* @throws IOException
*/
- public S3SecretValue assignUserToTenant(
+ // TODO: Rename this to tenantAssignUserAccessId ?
+ public S3SecretValue tenantAssignUserAccessId(
String username, String tenantName, String accessId) throws IOException {
- return proxy.assignUserToTenant(username, tenantName, accessId);
+ return proxy.tenantAssignUserAccessId(username, tenantName, accessId);
+ }
+
+ /**
+ * Revoke user accessId to tenant.
+ * @param accessId accessId to be revoked.
+ * @throws IOException
+ */
+ public void tenantRevokeUserAccessId(String accessId) throws IOException {
+ proxy.tenantRevokeUserAccessId(accessId);
+ }
+
+ /**
+ * Assign admin role to an accessId in a tenant.
+ * @param accessId access ID.
+ * @param tenantName tenant name.
+ * @param delegated true if making delegated admin.
+ * @throws IOException
+ */
+ public void tenantAssignAdmin(String accessId, String tenantName,
+ boolean delegated) throws IOException {
+ proxy.tenantAssignAdmin(accessId, tenantName, delegated);
+ }
+
+ /**
+ * Revoke admin role of an accessId from a tenant.
+ * @param accessId access ID.
+ * @param tenantName tenant name.
+ * @throws IOException
+ */
+ public void tenantRevokeAdmin(String accessId, String tenantName)
+ throws IOException {
+ proxy.tenantRevokeAdmin(accessId, tenantName);
}
/**
@@ -222,6 +254,15 @@ public class ObjectStore {
}
/**
+ * List tenants.
+ * @return TenantInfoList
+ * @throws IOException
+ */
+ public TenantInfoList listTenant() throws IOException {
+ return proxy.listTenant();
+ }
+
+ /**
* Returns Iterator to iterate over all the volumes in object store.
* The result can be restricted using volume prefix, will return all
* volumes if volume prefix is null.
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 69938aa..3845250 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
@@ -47,6 +47,7 @@ import org.apache.hadoop.ozone.om.helpers.OmMultipartUploadCompleteInfo;
import org.apache.hadoop.ozone.om.helpers.OzoneFileStatus;
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.protocol.OzoneManagerProtocol;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRoleInfo;
@@ -588,10 +589,35 @@ public interface ClientProtocol {
* @param accessId access ID.
* @throws IOException
*/
- S3SecretValue assignUserToTenant(String username, String tenantName,
+ S3SecretValue tenantAssignUserAccessId(String username, String tenantName,
String accessId) throws IOException;
/**
+ * Revoke user accessId to tenant.
+ * @param accessId accessId to be revoked.
+ * @throws IOException
+ */
+ void tenantRevokeUserAccessId(String accessId) throws IOException;
+
+ /**
+ * Assign admin role to an accessId in a tenant.
+ * @param accessId access ID.
+ * @param tenantName tenant name.
+ * @param delegated true if making delegated admin.
+ * @throws IOException
+ */
+ void tenantAssignAdmin(String accessId, String tenantName,
+ boolean delegated) throws IOException;
+
+ /**
+ * Revoke admin role of an accessId from a tenant.
+ * @param accessId access ID.
+ * @param tenantName tenant name.
+ * @throws IOException
+ */
+ void tenantRevokeAdmin(String accessId, String tenantName) throws IOException;
+
+ /**
* Get tenant info for a user.
* @param userPrincipal Kerberos principal of a user.
* @return TenantUserInfo
@@ -601,6 +627,13 @@ public interface ClientProtocol {
throws IOException;
/**
+ * List tenants.
+ * @return TenantInfoList
+ * @throws IOException
+ */
+ TenantInfoList listTenant() throws IOException;
+
+ /**
* Get KMS client provider.
* @return KMS client provider.
* @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 146db37..eabbfbb 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
@@ -104,6 +104,7 @@ import org.apache.hadoop.ozone.om.helpers.RepeatedOmKeyInfo;
import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
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.protocol.OzoneManagerProtocol;
import org.apache.hadoop.ozone.om.protocolPB.OmTransport;
@@ -645,8 +646,6 @@ public class RpcClient implements ClientProtocol {
ozoneManagerClient.createTenant(tenantName);
}
- // TODO: modify, delete
-
/**
* Assign user to tenant.
* @param username user name to be assigned.
@@ -654,7 +653,7 @@ public class RpcClient implements ClientProtocol {
* @throws IOException
*/
@Override
- public S3SecretValue assignUserToTenant(
+ public S3SecretValue tenantAssignUserAccessId(
String username, String tenantName, String accessId) throws IOException {
Preconditions.checkArgument(Strings.isNotBlank(username),
"username can't be null or empty.");
@@ -662,11 +661,55 @@ public class RpcClient implements ClientProtocol {
"tenantName can't be null or empty.");
Preconditions.checkArgument(Strings.isNotBlank(accessId),
"accessId can't be null or empty.");
- return ozoneManagerClient.assignUserToTenant(
+ return ozoneManagerClient.tenantAssignUserAccessId(
username, tenantName, accessId);
}
/**
+ * Revoke user accessId to tenant.
+ * @param accessId accessId to be revoked.
+ * @throws IOException
+ */
+ @Override
+ public void tenantRevokeUserAccessId(String accessId) throws IOException {
+ Preconditions.checkArgument(Strings.isNotBlank(accessId),
+ "accessId can't be null or empty.");
+ ozoneManagerClient.tenantRevokeUserAccessId(accessId);
+ }
+
+ /**
+ * Assign admin role to an accessId in a tenant.
+ * @param accessId access ID.
+ * @param tenantName tenant name.
+ * @param delegated true if making delegated admin.
+ * @throws IOException
+ */
+ @Override
+ public void tenantAssignAdmin(String accessId, String tenantName,
+ boolean delegated)
+ throws IOException {
+ Preconditions.checkArgument(Strings.isNotBlank(accessId),
+ "accessId can't be null or empty.");
+ // tenantName can be empty
+ ozoneManagerClient.tenantAssignAdmin(accessId, tenantName, delegated);
+ }
+
+ /**
+ * Revoke admin role of an accessId from a tenant.
+ * @param accessId access ID.
+ * @param tenantName tenant name.
+ * @throws IOException
+ */
+ @Override
+ public void tenantRevokeAdmin(String accessId, String tenantName)
+ throws IOException {
+ Preconditions.checkArgument(Strings.isNotBlank(accessId),
+ "accessId can't be null or empty.");
+ // tenantName can be empty
+ ozoneManagerClient.tenantRevokeAdmin(accessId, tenantName);
+ }
+
+ /**
* Get tenant info for a user.
* @param userPrincipal Kerberos principal of a user.
* @return TenantUserInfo
@@ -680,6 +723,16 @@ public class RpcClient implements ClientProtocol {
return ozoneManagerClient.tenantGetUserInfo(userPrincipal);
}
+ /**
+ * List tenants.
+ * @return TenantInfoList
+ * @throws IOException
+ */
+ @Override
+ public TenantInfoList listTenant() throws IOException {
+ return ozoneManagerClient.listTenant();
+ }
+
@Override
public void setBucketVersioning(
String volumeName, String bucketName, Boolean versioning)
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 e9255a3..8e80634 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
@@ -266,6 +266,7 @@ public final class OmUtils {
case FinalizeUpgradeProgress:
case PrepareStatus:
case GetS3Volume:
+ case ListTenant:
case TenantGetUserInfo:
return true;
case CreateVolume:
@@ -305,8 +306,10 @@ public final class OmUtils {
case CreateTenant:
case ModifyTenant:
case DeleteTenant:
- case AssignUserToTenant:
- case RevokeUserAccessToTenant:
+ case TenantAssignUserAccessId:
+ case TenantRevokeUserAccessId:
+ case TenantAssignAdmin:
+ case TenantRevokeAdmin:
return false;
default:
LOG.error("CmdType {} is not categorized as readOnly or not.", cmdType);
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 961aacb..5d37413 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
@@ -76,10 +76,14 @@ public enum OMAction implements AuditAction {
CREATE_TENANT,
MODIFY_TENANT,
DELETE_TENANT,
+ LIST_TENANT,
TENANT_GET_USER_INFO,
- ASSIGN_USER_TO_TENANT,
- REVOKE_USER_ACCESS_TO_TENANT;
+ TENANT_ASSIGN_USER_ACCESSID,
+ TENANT_REVOKE_USER_ACCESSID,
+
+ TENANT_ASSIGN_ADMIN,
+ TENANT_REVOKE_ADMIN;
@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 149aaf0..7b8502e 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
@@ -248,8 +248,9 @@ public class OMException extends IOException {
TENANT_ALREADY_EXISTS,
INVALID_TENANT_NAME,
- TENANT_USER_NOT_FOUND,
- TENANT_USER_ALREADY_EXISTS,
- INVALID_TENANT_USER_NAME
+ TENANT_USER_ACCESSID_NOT_FOUND,
+ TENANT_USER_ACCESSID_ALREADY_EXISTS,
+ INVALID_TENANT_USER_NAME,
+ INVALID_ACCESSID
}
}
diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmDBAccessIdInfo.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmDBAccessIdInfo.java
index a9c5366..69b4404 100644
--- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmDBAccessIdInfo.java
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmDBAccessIdInfo.java
@@ -33,28 +33,47 @@ public final class OmDBAccessIdInfo {
*/
private final String kerberosPrincipal;
/**
- * Shared secret of the accessId. TODO: Encryption?
+ * Shared secret of the accessId.
*/
private final String sharedSecret;
+ /**
+ * Whether this accessId is an administrator of the tenant.
+ */
+ private final boolean isAdmin;
+ /**
+ * Whether this accessId is a delegated admin of the tenant.
+ * Only effective if isAdmin is true.
+ */
+ private final boolean isDelegatedAdmin;
// This implies above String fields should NOT contain the split key.
public static final String SERIALIZATION_SPLIT_KEY = ";";
public OmDBAccessIdInfo(String tenantId,
- String kerberosPrincipal, String sharedSecret) {
+ String kerberosPrincipal, String sharedSecret,
+ boolean isAdmin, boolean isDelegatedAdmin) {
this.tenantId = tenantId;
this.kerberosPrincipal = kerberosPrincipal;
this.sharedSecret = sharedSecret;
+ this.isAdmin = isAdmin;
+ this.isDelegatedAdmin = isDelegatedAdmin;
}
private OmDBAccessIdInfo(String accessIdInfoString) {
String[] tInfo = accessIdInfoString.split(SERIALIZATION_SPLIT_KEY);
- Preconditions.checkState(tInfo.length == 3,
+ Preconditions.checkState(tInfo.length == 3 || tInfo.length == 5,
"Incorrect accessIdInfoString");
tenantId = tInfo[0];
kerberosPrincipal = tInfo[1];
sharedSecret = tInfo[2];
+ if (tInfo.length == 5) {
+ isAdmin = Boolean.parseBoolean(tInfo[3]);
+ isDelegatedAdmin = Boolean.parseBoolean(tInfo[4]);
+ } else {
+ isAdmin = false;
+ isDelegatedAdmin = false;
+ }
}
public String getTenantId() {
@@ -62,10 +81,12 @@ public final class OmDBAccessIdInfo {
}
private String serialize() {
- StringBuilder sb = new StringBuilder();
- sb.append(tenantId).append(SERIALIZATION_SPLIT_KEY);
- sb.append(kerberosPrincipal).append(SERIALIZATION_SPLIT_KEY);
- sb.append(sharedSecret);
+ final StringBuilder sb = new StringBuilder();
+ sb.append(tenantId);
+ sb.append(SERIALIZATION_SPLIT_KEY).append(kerberosPrincipal);
+ sb.append(SERIALIZATION_SPLIT_KEY).append(sharedSecret);
+ sb.append(SERIALIZATION_SPLIT_KEY).append(isAdmin);
+ sb.append(SERIALIZATION_SPLIT_KEY).append(isDelegatedAdmin);
return sb.toString();
}
@@ -93,6 +114,14 @@ public final class OmDBAccessIdInfo {
return sharedSecret;
}
+ public boolean getIsAdmin() {
+ return isAdmin;
+ }
+
+ public boolean getIsDelegatedAdmin() {
+ return isDelegatedAdmin;
+ }
+
/**
* Builder for OmDBAccessIdInfo.
*/
@@ -101,8 +130,10 @@ public final class OmDBAccessIdInfo {
private String tenantId;
private String kerberosPrincipal;
private String sharedSecret;
+ private boolean isAdmin;
+ private boolean isDelegatedAdmin;
- public Builder setTenantName(String tenantId) {
+ public Builder setTenantId(String tenantId) {
this.tenantId = tenantId;
return this;
}
@@ -117,8 +148,19 @@ public final class OmDBAccessIdInfo {
return this;
}
+ public Builder setIsAdmin(boolean isAdmin) {
+ this.isAdmin = isAdmin;
+ return this;
+ }
+
+ public Builder setIsDelegatedAdmin(boolean isDelegatedAdmin) {
+ this.isDelegatedAdmin = isDelegatedAdmin;
+ return this;
+ }
+
public OmDBAccessIdInfo build() {
- return new OmDBAccessIdInfo(tenantId, kerberosPrincipal, sharedSecret);
+ return new OmDBAccessIdInfo(tenantId, kerberosPrincipal, sharedSecret,
+ isAdmin, isDelegatedAdmin);
}
}
}
diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmDBKerberosPrincipalInfo.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmDBKerberosPrincipalInfo.java
index a231ad4..c5274fd 100644
--- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmDBKerberosPrincipalInfo.java
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmDBKerberosPrincipalInfo.java
@@ -22,6 +22,7 @@ import org.apache.hadoop.hdds.StringUtils;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
+import java.util.stream.Collectors;
/**
* This class is used for storing info related to the Kerberos principal.
@@ -45,8 +46,9 @@ public final class OmDBKerberosPrincipalInfo {
}
private OmDBKerberosPrincipalInfo(String serialized) {
- accessIds = new HashSet<>(
- Arrays.asList(serialized.split(SERIALIZATION_SPLIT_KEY)));
+ accessIds = Arrays.stream(serialized.split(SERIALIZATION_SPLIT_KEY))
+ // Remove any empty accessId strings when deserializing
+ .filter(e -> !e.isEmpty()).collect(Collectors.toSet());
}
public Set<String> getAccessIds() {
diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/TenantInfoList.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/TenantInfoList.java
new file mode 100644
index 0000000..c37f989
--- /dev/null
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/TenantInfoList.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.om.helpers;
+
+import org.apache.commons.lang3.NotImplementedException;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantInfo;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Utility class to handle protobuf message TenantInfo conversion.
+ */
+public class TenantInfoList {
+
+ // A list of TenantAccessIdInfo from protobuf.
+ private final List<TenantInfo> tenantInfoList;
+
+ public List<TenantInfo> getTenantInfoList() {
+ return tenantInfoList;
+ }
+
+ public TenantInfoList(List<TenantInfo> tenantInfoList) {
+ this.tenantInfoList = tenantInfoList;
+ }
+
+ public static TenantInfoList fromProtobuf(List<TenantInfo> tenantInfoList) {
+ return new TenantInfoList(tenantInfoList);
+ }
+
+ public TenantInfo getProtobuf() {
+ throw new NotImplementedException("getProtobuf() not implemented");
+ }
+
+ @Override
+ public String toString() {
+ return "tenantInfoList=" + tenantInfoList;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ TenantInfoList that = (TenantInfoList) o;
+ return tenantInfoList.equals(that.tenantInfoList);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(tenantInfoList);
+ }
+}
diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/lock/OzoneManagerLock.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/lock/OzoneManagerLock.java
index ca3de18..2c03573 100644
--- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/lock/OzoneManagerLock.java
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/lock/OzoneManagerLock.java
@@ -390,6 +390,7 @@ public class OzoneManagerLock {
S3_SECRET_LOCK((byte) 4, "S3_SECRET_LOCK"), // 31
PREFIX_LOCK((byte) 5, "PREFIX_LOCK"); //63
+// TENANT_LOCK((byte) 6, "TENANT_LOCK"); // 127
// level of the resource
private byte lockLevel;
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/CephCompatibleTenantImpl.java
index e5b42c9..03d2b6a 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/CephCompatibleTenantImpl.java
@@ -33,16 +33,15 @@ import org.apache.hadoop.ozone.security.acl.OzoneObjInfo;
*/
public class CephCompatibleTenantImpl implements Tenant {
private final String tenantID;
- private List<String> tenantGroupsIDs;
+ private List<String> tenantRoleIds;
private List<AccessPolicy> accessPolicies;
private final AccountNameSpace accountNameSpace;
private final BucketNameSpace bucketNameSpace;
-
public CephCompatibleTenantImpl(String id) {
tenantID = id;
accessPolicies = new ArrayList<>();
- tenantGroupsIDs = new ArrayList<>();
+ tenantRoleIds = new ArrayList<>();
accountNameSpace = new AccountNameSpaceImpl(id);
bucketNameSpace = new BucketNameSpaceImpl(id);
OzoneObj volume = new OzoneObjInfo.Builder()
@@ -83,18 +82,17 @@ public class CephCompatibleTenantImpl implements Tenant {
}
@Override
- public void addTenantAccessGroup(String groupID) {
- tenantGroupsIDs.add(groupID);
-
+ public void addTenantAccessRole(String roleId) {
+ tenantRoleIds.add(roleId);
}
@Override
- public void removeTenantAccessGroup(String groupID) {
- tenantGroupsIDs.remove(groupID);
+ public void removeTenantAccessRole(String roleId) {
+ tenantRoleIds.remove(roleId);
}
@Override
- public List<String> getTenantGroups() {
- return tenantGroupsIDs;
+ public List<String> getTenantRoles() {
+ return tenantRoleIds;
}
}
diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/MultiTenantAccessAuthorizer.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/MultiTenantAccessAuthorizer.java
index 5f0a9fd..72c41ed 100644
--- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/MultiTenantAccessAuthorizer.java
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/MultiTenantAccessAuthorizer.java
@@ -53,30 +53,32 @@ public interface MultiTenantAccessAuthorizer extends IAccessAuthorizer {
void shutdown() throws Exception;
/**
- * Create User Principal entity for MultiTenantGatekeeper plugin.
- * @param principal
- * @param groupIDs : groupIDs that this user will belong to
+ * Assign user to an existing role in the Authorizer.
+ * @param principal User principal
+ * @param existingRole A JSON String representation of the existing role
+ * returned from the Authorizer (Ranger).
+ * @param isAdmin
* @return unique and opaque userID that can be used to refer to the user in
* MultiTenantGateKeeperplugin Implementation. E.g. a Ranger
* based Implementation can return some ID thats relevant for it.
*/
- String createUser(BasicUserPrincipal principal,
- List<String> groupIDs) throws Exception;
+ String assignUser(BasicUserPrincipal principal, String existingRole,
+ boolean isAdmin) throws IOException;
/**
* @param principal
* @return Unique userID maintained by the authorizer plugin.
- * @throws Exception
+ * @throws IOException
*/
- String getUserId(BasicUserPrincipal principal) throws Exception;
+ String getUserId(BasicUserPrincipal principal) throws IOException;
/**
* @param principal
* @return Unique groupID maintained by the authorizer plugin.
- * @throws Exception
+ * @throws IOException
*/
- String getGroupId(OzoneTenantGroupPrincipal principal)
- throws Exception;
+ String getRole(OzoneTenantRolePrincipal principal)
+ throws IOException;
/**
* Delete the user userID in MultiTenantGateKeeper plugin.
@@ -87,20 +89,23 @@ public interface MultiTenantAccessAuthorizer extends IAccessAuthorizer {
void deleteUser(String opaqueUserID) throws IOException;
/**
- * Create Group group entity for MultiTenantGatekeeper plugin.
- * @param group
- * @return unique groupID that can be used to refer to the group in
+ * Create Role entity for MultiTenantGatekeeper plugin.
+ * @param role
+ * @param adminRoleName (Optional) admin role name that will be added to
+ * manage this role.
+ * @return unique groupID that can be used to refer to the role in
* MultiTenantGateKeeper plugin Implementation e.g. corresponding ID on the
* Ranger end for a ranger based implementation .
*/
- String createGroup(OzoneTenantGroupPrincipal group) throws Exception;
+ String createRole(OzoneTenantRolePrincipal role, String adminRoleName)
+ throws IOException;
/**
* Delete the group groupID in MultiTenantGateKeeper plugin.
* @param groupID : unique opaque ID that was returned by
* MultiTenantGatekeeper in createGroup().
*/
- void deleteGroup(String groupID) throws IOException;
+ void deleteRole(String groupID) throws IOException;
/**
*
diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/MultiTenantAccessAuthorizerDummyPlugin.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/MultiTenantAccessAuthorizerDummyPlugin.java
index 3f23a42..582839c 100644
--- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/MultiTenantAccessAuthorizerDummyPlugin.java
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/MultiTenantAccessAuthorizerDummyPlugin.java
@@ -45,19 +45,18 @@ public class MultiTenantAccessAuthorizerDummyPlugin implements
}
@Override
- public String createUser(BasicUserPrincipal principal, List<String> groupIDs)
- throws Exception {
- return null;
+ public String assignUser(BasicUserPrincipal principal, String existingRole,
+ boolean isAdmin) throws IOException {
+ return "assignUser-roleId-returned";
}
@Override
- public String getUserId(BasicUserPrincipal principal) throws Exception {
+ public String getUserId(BasicUserPrincipal principal) throws IOException {
return null;
}
@Override
- public String getGroupId(OzoneTenantGroupPrincipal principal)
- throws Exception {
+ public String getRole(OzoneTenantRolePrincipal principal) throws IOException {
return null;
}
@@ -67,12 +66,13 @@ public class MultiTenantAccessAuthorizerDummyPlugin implements
}
@Override
- public String createGroup(OzoneTenantGroupPrincipal group) throws Exception {
+ public String createRole(OzoneTenantRolePrincipal role, String adminRoleName)
+ throws IOException {
return null;
}
@Override
- public void deleteGroup(String groupID) throws IOException {
+ public void deleteRole(String groupID) throws IOException {
}
diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/MultiTenantAccessAuthorizerRangerPlugin.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/MultiTenantAccessAuthorizerRangerPlugin.java
index 94a31dd..b8f5b4e 100644
--- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/MultiTenantAccessAuthorizerRangerPlugin.java
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/MultiTenantAccessAuthorizerRangerPlugin.java
@@ -17,15 +17,16 @@
*/
package org.apache.hadoop.ozone.om.multitenant;
-import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ADMIN_CREATE_GROUP_HTTP_ENDPOINT;
+import static java.net.HttpURLConnection.HTTP_OK;
import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ADMIN_CREATE_POLICY_HTTP_ENDPOINT;
-import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ADMIN_CREATE_USER_HTTP_ENDPOINT;
+import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ADMIN_CREATE_ROLE_HTTP_ENDPOINT;
import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ADMIN_DELETE_GROUP_HTTP_ENDPOINT;
import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ADMIN_DELETE_POLICY_HTTP_ENDPOINT;
import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ADMIN_DELETE_USER_HTTP_ENDPOINT;
-import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ADMIN_GET_GROUP_HTTP_ENDPOINT;
import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ADMIN_GET_POLICY_HTTP_ENDPOINT;
+import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ADMIN_GET_ROLE_HTTP_ENDPOINT;
import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ADMIN_GET_USER_HTTP_ENDPOINT;
+import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ADMIN_ROLE_ADD_USER_HTTP_ENDPOINT;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_RANGER_HTTPS_ADMIN_API_PASSWD;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_RANGER_HTTPS_ADMIN_API_USER;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_HTTPS_ADDRESS_KEY;
@@ -44,7 +45,6 @@ import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
@@ -62,17 +62,16 @@ import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.security.acl.IOzoneObj;
import org.apache.hadoop.ozone.security.acl.RequestContext;
-import org.apache.hadoop.security.authentication.client.AuthenticationException;
import org.apache.http.auth.BasicUserPrincipal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * Implements MultiTenantAccessAuthorizer.
+ * Implements MultiTenantAccessAuthorizer for Apache Ranger.
*/
public class MultiTenantAccessAuthorizerRangerPlugin implements
MultiTenantAccessAuthorizer {
- private static final Logger LOG = LoggerFactory
+ public static final Logger LOG = LoggerFactory
.getLogger(MultiTenantAccessAuthorizerRangerPlugin.class);
private OzoneConfiguration conf;
@@ -207,55 +206,20 @@ public class MultiTenantAccessAuthorizerRangerPlugin implements
// TBD
return true;
}
- private String getCreateUserJsonString(String userName,
- List<String> groupIDs)
- throws Exception {
- String groupIdList = groupIDs.stream().collect(Collectors.joining("\",\"",
- "", ""));
- String jsonCreateUserString = "{ \"name\":\"" + userName + "\"," +
- "\"firstName\":\"" + userName + "\"," +
- " \"loginId\": \"" + userName + "\"," +
- " \"password\" : \"user1pass\"," +
- " \"userRoleList\":[\"ROLE_USER\"]," +
- " \"groupIdList\":[\"" + groupIdList +"\"] " +
- " }";
- return jsonCreateUserString;
- }
@Override
- public String getGroupId(OzoneTenantGroupPrincipal principal)
- throws Exception {
- String rangerAdminUrl =
- rangerHttpsAddress + OZONE_OM_RANGER_ADMIN_GET_GROUP_HTTP_ENDPOINT +
+ public String getRole(OzoneTenantRolePrincipal principal) throws IOException {
+
+ String endpointUrl =
+ rangerHttpsAddress + OZONE_OM_RANGER_ADMIN_GET_ROLE_HTTP_ENDPOINT +
principal.getName();
- HttpsURLConnection conn = makeHttpsGetCall(rangerAdminUrl,
- "GET", false);
- String response = getResponseData(conn);
- String groupIDCreated = null;
- try {
- JsonObject jResonse = new JsonParser().parse(response).getAsJsonObject();
- JsonArray info = jResonse.get("vXGroups").getAsJsonArray();
- int numIndex = info.size();
- for (int i = 0; i < numIndex; ++i) {
- if (info.get(i).getAsJsonObject().get("name").getAsString()
- .equals(principal.getName())) {
- groupIDCreated =
- info.get(i).getAsJsonObject().get("id").getAsString();
- break;
- }
- }
- System.out.println("Group ID is : " + groupIDCreated);
- } catch (JsonParseException e) {
- e.printStackTrace();
- throw e;
- }
- return groupIDCreated;
+ HttpsURLConnection conn = makeHttpsGetCall(endpointUrl, "GET", false);
+ return getResponseData(conn);
}
@Override
- public String getUserId(BasicUserPrincipal principal)
- throws Exception {
+ public String getUserId(BasicUserPrincipal principal) throws IOException {
String rangerAdminUrl =
rangerHttpsAddress + OZONE_OM_RANGER_ADMIN_GET_USER_HTTP_ENDPOINT +
principal.getName();
@@ -276,7 +240,7 @@ public class MultiTenantAccessAuthorizerRangerPlugin implements
break;
}
}
- System.out.println("User ID is : " + userIDCreated);
+ LOG.debug("User ID is: {}", userIDCreated);
} catch (JsonParseException e) {
e.printStackTrace();
throw e;
@@ -284,65 +248,99 @@ public class MultiTenantAccessAuthorizerRangerPlugin implements
return userIDCreated;
}
- public String createUser(BasicUserPrincipal principal,
- List<String> groupIDs)
- throws Exception {
- String rangerAdminUrl =
- rangerHttpsAddress + OZONE_OM_RANGER_ADMIN_CREATE_USER_HTTP_ENDPOINT;
-
- String jsonCreateUserString = getCreateUserJsonString(
- principal.getName(), groupIDs);
-
- HttpsURLConnection conn = makeHttpsPostCall(rangerAdminUrl,
- jsonCreateUserString, "POST", false);
- String userInfo = getResponseData(conn);
- String userIDCreated;
+ /**
+ * Update the exising role details and push the changes to Ranger.
+ *
+ * @param principal contains user name, must be an existing user in Ranger.
+ * @param existingRole An existing role's JSON response String from Ranger.
+ * @param isAdmin Make it delegated admin of the role.
+ * @return roleId (not useful for now)
+ * @throws IOException
+ */
+ public String assignUser(BasicUserPrincipal principal, String existingRole,
+ boolean isAdmin) throws IOException {
+
+ JsonObject roleObj = new JsonParser().parse(existingRole).getAsJsonObject();
+ // Parse Json
+ final String roleId = roleObj.get("id").getAsString();
+ LOG.debug("Got roleId: {}", roleId);
+
+ JsonArray usersArray = roleObj.getAsJsonArray("users");
+ JsonObject newUserEntry = new JsonObject();
+ newUserEntry.addProperty("name", principal.getName());
+ newUserEntry.addProperty("isAdmin", isAdmin);
+ usersArray.add(newUserEntry);
+ // Update Json array
+ roleObj.add("users", usersArray);
+
+ LOG.debug("Updated: {}", roleObj);
+
+ final String endpointUrl = rangerHttpsAddress +
+ OZONE_OM_RANGER_ADMIN_ROLE_ADD_USER_HTTP_ENDPOINT + roleId;
+ final String jsonData = roleObj.toString();
+
+ HttpsURLConnection conn =
+ makeHttpCall(endpointUrl, jsonData, "PUT", false);
+ if (conn.getResponseCode() != HTTP_OK) {
+ throw new IOException("Ranger REST API failure: " + conn.getResponseCode()
+ + " " + conn.getResponseMessage()
+ + ". Error updating Ranger role.");
+ }
+ String resp = getResponseData(conn);
+ String returnedRoleId;
try {
- JsonObject jObject = new JsonParser().parse(userInfo).getAsJsonObject();
- userIDCreated = jObject.get("id").getAsString();
- System.out.println("User ID is : " + userIDCreated);
+ JsonObject jObject = new JsonParser().parse(resp).getAsJsonObject();
+ returnedRoleId = jObject.get("id").getAsString();
+ LOG.debug("Ranger returns roleId: {}", roleId);
} catch (JsonParseException e) {
e.printStackTrace();
throw e;
}
- return userIDCreated;
+ return returnedRoleId;
}
- private String getCreateGroupJsonString(String groupName) throws Exception {
- String jsonCreateGroupString = "{ \"name\":\"" + groupName + "\"," +
- " \"description\":\"test\" " +
- " }";
- return jsonCreateGroupString;
+ private String getCreateRoleJsonStr(String roleName, String adminRoleName) {
+ return "{"
+ + " \"name\":\"" + roleName + "\","
+ + " \"description\":\"Role created by Ozone for Multi-Tenancy\""
+ + (adminRoleName == null ? "" : ", \"roles\":"
+ + "[{\"name\":\"" + adminRoleName + "\",\"isAdmin\": true}]")
+ + "}";
}
+ public String createRole(OzoneTenantRolePrincipal role, String adminRoleName)
+ throws IOException {
- public String createGroup(OzoneTenantGroupPrincipal group) throws Exception {
- String rangerAdminUrl =
- rangerHttpsAddress + OZONE_OM_RANGER_ADMIN_CREATE_GROUP_HTTP_ENDPOINT;
+ String endpointUrl =
+ rangerHttpsAddress + OZONE_OM_RANGER_ADMIN_CREATE_ROLE_HTTP_ENDPOINT;
- String jsonCreateGroupString = getCreateGroupJsonString(group.toString());
+ String jsonData = getCreateRoleJsonStr(role.toString(), adminRoleName);
- HttpsURLConnection conn = makeHttpsPostCall(rangerAdminUrl,
- jsonCreateGroupString,
- "POST", false);
- String groupInfo = getResponseData(conn);
- String groupIdCreated;
+ final HttpsURLConnection conn = makeHttpCall(endpointUrl,
+ jsonData, "POST", false);
+ if (conn.getResponseCode() != HTTP_OK) {
+ throw new IOException("Ranger REST API failure: " + conn.getResponseCode()
+ + " " + conn.getResponseMessage()
+ + ". Role name '" + role + "' likely already exists in Ranger");
+ }
+ String roleInfo = getResponseData(conn);
+ String roleId;
try {
- JsonObject jObject = new JsonParser().parse(groupInfo).getAsJsonObject();
- groupIdCreated = jObject.get("id").getAsString();
- System.out.println("GroupID is: " + groupIdCreated);
+ JsonObject jObject = new JsonParser().parse(roleInfo).getAsJsonObject();
+ roleId = jObject.get("id").getAsString();
+ LOG.debug("Ranger returned roleId: {}", roleId);
} catch (JsonParseException e) {
e.printStackTrace();
throw e;
}
- return groupIdCreated;
+ return roleId;
}
public String createAccessPolicy(AccessPolicy policy) throws Exception {
String rangerAdminUrl =
rangerHttpsAddress + OZONE_OM_RANGER_ADMIN_CREATE_POLICY_HTTP_ENDPOINT;
- HttpsURLConnection conn = makeHttpsPostCall(rangerAdminUrl,
+ HttpsURLConnection conn = makeHttpCall(rangerAdminUrl,
policy.serializePolicyToJsonString(),
"POST", false);
String policyInfo = getResponseData(conn);
@@ -350,7 +348,7 @@ public class MultiTenantAccessAuthorizerRangerPlugin implements
try {
JsonObject jObject = new JsonParser().parse(policyInfo).getAsJsonObject();
policyID = jObject.get("id").getAsString();
- System.out.println("policyID is : " + policyID);
+ LOG.debug("policyID is: {}", policyID);
} catch (JsonParseException e) {
e.printStackTrace();
throw e;
@@ -380,7 +378,7 @@ public class MultiTenantAccessAuthorizerRangerPlugin implements
rangerHttpsAddress + OZONE_OM_RANGER_ADMIN_DELETE_USER_HTTP_ENDPOINT
+ userId + "?forceDelete=true";
- HttpsURLConnection conn = makeHttpsPostCall(rangerAdminUrl, null,
+ HttpsURLConnection conn = makeHttpCall(rangerAdminUrl, null,
"DELETE", false);
int respnseCode = conn.getResponseCode();
if (respnseCode != 200 && respnseCode != 204) {
@@ -388,13 +386,13 @@ public class MultiTenantAccessAuthorizerRangerPlugin implements
}
}
- public void deleteGroup(String groupId) throws IOException {
+ public void deleteRole(String groupId) throws IOException {
String rangerAdminUrl =
rangerHttpsAddress + OZONE_OM_RANGER_ADMIN_DELETE_GROUP_HTTP_ENDPOINT
+ groupId + "?forceDelete=true";
- HttpsURLConnection conn = makeHttpsPostCall(rangerAdminUrl, null,
+ HttpsURLConnection conn = makeHttpCall(rangerAdminUrl, null,
"DELETE", false);
int respnseCode = conn.getResponseCode();
if (respnseCode != 200 && respnseCode != 204) {
@@ -406,7 +404,7 @@ public class MultiTenantAccessAuthorizerRangerPlugin implements
public void deletePolicybyName(String policyName) throws Exception {
AccessPolicy policy = getAccessPolicyByName(policyName);
String policyID = policy.getPolicyID();
- System.out.println("policyID is : " + policyID);
+ LOG.debug("policyID is: {}", policyID);
deletePolicybyId(policyID);
}
@@ -416,7 +414,7 @@ public class MultiTenantAccessAuthorizerRangerPlugin implements
rangerHttpsAddress + OZONE_OM_RANGER_ADMIN_DELETE_POLICY_HTTP_ENDPOINT
+ policyId + "?forceDelete=true";
try {
- HttpsURLConnection conn = makeHttpsPostCall(rangerAdminUrl, null,
+ HttpsURLConnection conn = makeHttpCall(rangerAdminUrl, null,
"DELETE", false);
int respnseCode = conn.getResponseCode();
if (respnseCode != 200 && respnseCode != 204) {
@@ -431,12 +429,13 @@ public class MultiTenantAccessAuthorizerRangerPlugin implements
throws IOException {
StringBuilder response = new StringBuilder();
try (BufferedReader br = new BufferedReader(
- new InputStreamReader(urlConnection.getInputStream(), "utf-8"))) {
+ new InputStreamReader(urlConnection.getInputStream(),
+ StandardCharsets.UTF_8))) {
String responseLine;
while ((responseLine = br.readLine()) != null) {
response.append(responseLine.trim());
}
- System.out.println(response);
+ LOG.debug("Got response: {}", response);
} catch (Exception e) {
e.printStackTrace();
throw e;
@@ -444,7 +443,7 @@ public class MultiTenantAccessAuthorizerRangerPlugin implements
return response.toString();
}
- private HttpsURLConnection makeHttpsPostCall(String urlString,
+ private HttpsURLConnection makeHttpCall(String urlString,
String jsonInputString,
String method, boolean isSpnego)
throws IOException {
@@ -457,11 +456,11 @@ public class MultiTenantAccessAuthorizerRangerPlugin implements
urlConnection.setRequestProperty("Accept", "application/json");
urlConnection.setRequestProperty("Authorization", authHeaderValue);
- if ((jsonInputString !=null) && !jsonInputString.isEmpty()) {
+ if ((jsonInputString != null) && !jsonInputString.isEmpty()) {
urlConnection.setDoOutput(true);
urlConnection.setRequestProperty("Content-Type", "application/json;");
try (OutputStream os = urlConnection.getOutputStream()) {
- byte[] input = jsonInputString.getBytes("utf-8");
+ byte[] input = jsonInputString.getBytes(StandardCharsets.UTF_8);
os.write(input, 0, input.length);
os.flush();
}
@@ -471,8 +470,7 @@ public class MultiTenantAccessAuthorizerRangerPlugin implements
}
private HttpsURLConnection makeHttpsGetCall(String urlString,
- String method, boolean isSpnego)
- throws IOException, AuthenticationException {
+ String method, boolean isSpnego) throws IOException {
URL url = new URL(urlString);
HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection();
diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/RevokeUserAccessToTenantHandler.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/OzoneOwnerPrincipal.java
similarity index 64%
copy from hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/RevokeUserAccessToTenantHandler.java
copy to hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/OzoneOwnerPrincipal.java
index 0c4cdf7..6ad51c2 100644
--- a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/RevokeUserAccessToTenantHandler.java
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/OzoneOwnerPrincipal.java
@@ -15,21 +15,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.hadoop.ozone.shell.tenant;
+package org.apache.hadoop.ozone.om.multitenant;
-import org.apache.hadoop.ozone.client.OzoneClient;
-import org.apache.hadoop.ozone.shell.OzoneAddress;
-import picocli.CommandLine;
+import java.security.Principal;
/**
- * ozone tenant user revoke.
+ * Used to specify {OWNER} tag in Ranger.
*/
-@CommandLine.Command(name = "revoke",
- description = "Revoke user access to tenant")
-public class RevokeUserAccessToTenantHandler extends TenantHandler {
+public final class OzoneOwnerPrincipal implements Principal {
+
+ public OzoneOwnerPrincipal() {
+ }
+
+ @Override
+ public String toString() {
+ return getName();
+ }
@Override
- protected void execute(OzoneClient client, OzoneAddress address) {
- out().println("Not Implemented.");
+ public String getName() {
+ return "{OWNER}";
}
}
diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/OzoneTenantGroupPrincipal.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/OzoneTenantRolePrincipal.java
similarity index 65%
rename from hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/OzoneTenantGroupPrincipal.java
rename to hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/OzoneTenantRolePrincipal.java
index c264ad7..3aa3e3d 100644
--- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/OzoneTenantGroupPrincipal.java
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/OzoneTenantRolePrincipal.java
@@ -21,23 +21,23 @@ import org.apache.hadoop.ozone.OzoneConsts;
import java.security.Principal;
/**
- * Used to identify a tenant's group in Ranger.
+ * Used to identify a tenant's role in Ranger.
*/
-public final class OzoneTenantGroupPrincipal implements Principal {
+public final class OzoneTenantRolePrincipal implements Principal {
private final String tenantID;
- private final String groupName;
+ private final String roleName;
- public static OzoneTenantGroupPrincipal newUserGroup(String tenantID) {
- return new OzoneTenantGroupPrincipal(tenantID, "GroupTenantAllUsers");
+ public static OzoneTenantRolePrincipal getUserRole(String tenantID) {
+ return new OzoneTenantRolePrincipal(tenantID, "UserRole");
}
- public static OzoneTenantGroupPrincipal newAdminGroup(String tenantID) {
- return new OzoneTenantGroupPrincipal(tenantID, "GroupTenantAllAdmins");
+ public static OzoneTenantRolePrincipal getAdminRole(String tenantID) {
+ return new OzoneTenantRolePrincipal(tenantID, "AdminRole");
}
- private OzoneTenantGroupPrincipal(String tenantID, String groupName) {
+ private OzoneTenantRolePrincipal(String tenantID, String roleName) {
this.tenantID = tenantID;
- this.groupName = groupName;
+ this.roleName = roleName;
}
public String getTenantID() {
@@ -51,6 +51,6 @@ public final class OzoneTenantGroupPrincipal implements Principal {
@Override
public String getName() {
- return tenantID + OzoneConsts.TENANT_NAME_USER_NAME_DELIMITER + groupName;
+ return tenantID + OzoneConsts.TENANT_NAME_ROLE_DELIMITER + roleName;
}
}
diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/RangerAccessPolicy.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/RangerAccessPolicy.java
index 113bd99..f05f237 100644
--- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/RangerAccessPolicy.java
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/RangerAccessPolicy.java
@@ -192,8 +192,8 @@ public class RangerAccessPolicy implements AccessPolicy {
continue;
}
policyItems.append("{");
- if (list.get(0).getPrincipal() instanceof OzoneTenantGroupPrincipal) {
- policyItems.append("\"groups\":[\"" + mapElem.getKey() + "\"],");
+ if (list.get(0).getPrincipal() instanceof OzoneTenantRolePrincipal) {
+ policyItems.append("\"roles\":[\"" + mapElem.getKey() + "\"],");
} else {
policyItems.append("\"users\":[\"" + mapElem.getKey() + "\"],");
}
diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/Tenant.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/Tenant.java
index cd2ab4b..73e1f29 100644
--- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/Tenant.java
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/Tenant.java
@@ -51,9 +51,9 @@ public interface Tenant {
void removeTenantAccessPolicy(AccessPolicy policy);
- void addTenantAccessGroup(String groupID);
+ void addTenantAccessRole(String groupID);
- void removeTenantAccessGroup(String groupID);
+ void removeTenantAccessRole(String groupID);
- List<String> getTenantGroups();
+ List<String> getTenantRoles();
}
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 ae5f276..9063dd5 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
@@ -47,6 +47,7 @@ import org.apache.hadoop.ozone.om.helpers.RepeatedOmKeyInfo;
import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
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.protocol.proto.OzoneManagerProtocolProtos;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OzoneAclInfo;
@@ -472,13 +473,38 @@ public interface OzoneManagerProtocol
* @param accessId access ID.
* @throws IOException
*/
- S3SecretValue assignUserToTenant(String username, String tenantName,
+ S3SecretValue tenantAssignUserAccessId(String username, String tenantName,
String accessId) throws IOException;
OmVolumeArgs getS3Volume(String accessID) throws IOException;
// TODO: modify, delete
/**
+ * Revoke user accessId to tenant.
+ * @param accessId accessId to be revoked.
+ * @throws IOException
+ */
+ void tenantRevokeUserAccessId(String accessId) throws IOException;
+
+ /**
+ * Assign admin role to an accessId in a tenant.
+ * @param accessId access ID.
+ * @param tenantName tenant name.
+ * @param delegated true if making delegated admin.
+ * @throws IOException
+ */
+ void tenantAssignAdmin(String accessId, String tenantName,
+ boolean delegated) throws IOException;
+
+ /**
+ * Revoke admin role of an accessId from a tenant.
+ * @param accessId access ID.
+ * @param tenantName tenant name.
+ * @throws IOException
+ */
+ void tenantRevokeAdmin(String accessId, String tenantName) throws IOException;
+
+ /**
* Get tenant info for a user.
* @param userPrincipal Kerberos principal of a user.
* @return TenantUserInfo
@@ -488,6 +514,13 @@ public interface OzoneManagerProtocol
throws IOException;
/**
+ * List tenants.
+ * @return TenantInfoList
+ * @throws IOException
+ */
+ TenantInfoList listTenant() throws IOException;
+
+ /**
* OzoneFS api to get file status for an entry.
*
* @param keyArgs Key args
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 94b1c9b..b01fe3a 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
@@ -56,6 +56,7 @@ import org.apache.hadoop.ozone.om.helpers.RepeatedOmKeyInfo;
import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
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.protocol.OzoneManagerProtocol;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.AddAclRequest;
@@ -74,8 +75,6 @@ import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateF
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateKeyRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateKeyResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantRequest;
-import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.AssignUserToTenantRequest;
-import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.AssignUserToTenantResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateVolumeRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DBUpdatesRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DBUpdatesResponse;
@@ -108,6 +107,8 @@ import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListMul
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListMultipartUploadsResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListStatusRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListStatusResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListTenantRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListTenantResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListTrashRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListTrashResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListVolumeRequest;
@@ -145,6 +146,12 @@ import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.SetAclR
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.SetAclResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.SetBucketPropertyRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.SetVolumePropertyRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantAssignAdminRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantAssignUserAccessIdRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantAssignUserAccessIdResponse;
+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.TenantRevokeUserAccessIdRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.VolumeInfo;
import org.apache.hadoop.ozone.protocolPB.OMPBHelper;
@@ -883,6 +890,8 @@ public final class OzoneManagerProtocolClientSideTranslatorPB
/**
* {@inheritDoc}
+ *
+ * TODO: Add a variant that uses OmTenantArgs?
*/
@Override
public void createTenant(String tenantArgs)
@@ -896,33 +905,96 @@ public final class OzoneManagerProtocolClientSideTranslatorPB
final OMResponse omResponse = submitRequest(omRequest);
handleError(omResponse);
}
- // TODO: Add a variant that uses OmTenantArgs
- // TODO: modify, delete
/**
* {@inheritDoc}
+ *
+ * TODO: Add a variant that uses OmTenantUserArgs?
*/
@Override
- public S3SecretValue assignUserToTenant(
+ public S3SecretValue tenantAssignUserAccessId(
String username, String tenantName, String accessId) throws IOException {
- final AssignUserToTenantRequest request =
- AssignUserToTenantRequest.newBuilder()
+ final TenantAssignUserAccessIdRequest request =
+ TenantAssignUserAccessIdRequest.newBuilder()
.setTenantUsername(username)
.setTenantName(tenantName)
.setAccessId(accessId)
.build();
- final OMRequest omRequest = createOMRequest(Type.AssignUserToTenant)
- .setAssignUserToTenantRequest(request)
+ final OMRequest omRequest = createOMRequest(Type.TenantAssignUserAccessId)
+ .setTenantAssignUserAccessIdRequest(request)
.build();
final OMResponse omResponse = submitRequest(omRequest);
- final AssignUserToTenantResponse resp = handleError(omResponse)
- .getAssignUserToTenantResponse();
+ final TenantAssignUserAccessIdResponse resp = handleError(omResponse)
+ .getTenantAssignUserAccessIdResponse();
return S3SecretValue.fromProtobuf(resp.getS3Secret());
}
- // TODO: Add a variant that uses OmTenantUserArgs?
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void tenantRevokeUserAccessId(String accessId)
+ throws IOException {
+
+ final TenantRevokeUserAccessIdRequest request =
+ TenantRevokeUserAccessIdRequest.newBuilder()
+ .setAccessId(accessId)
+ .build();
+ final OMRequest omRequest = createOMRequest(Type.TenantRevokeUserAccessId)
+ .setTenantRevokeUserAccessIdRequest(request)
+ .build();
+ final OMResponse omResponse = submitRequest(omRequest);
+ handleError(omResponse);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void tenantAssignAdmin(String accessId, String tenantName,
+ boolean delegated) throws IOException {
+
+ final TenantAssignAdminRequest.Builder requestBuilder =
+ TenantAssignAdminRequest.newBuilder()
+ .setAccessId(accessId)
+ .setDelegated(delegated);
+ if (tenantName != null) {
+ requestBuilder.setTenantName(tenantName);
+ }
+ final TenantAssignAdminRequest request = requestBuilder.build();
+ final OMRequest omRequest = createOMRequest(Type.TenantAssignAdmin)
+ .setTenantAssignAdminRequest(request)
+ .build();
+ final OMResponse omResponse = submitRequest(omRequest);
+ handleError(omResponse);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void tenantRevokeAdmin(String accessId, String tenantName)
+ throws IOException {
+
+ final TenantRevokeAdminRequest.Builder requestBuilder =
+ TenantRevokeAdminRequest.newBuilder()
+ .setAccessId(accessId);
+ if (tenantName != null) {
+ requestBuilder.setTenantName(tenantName);
+ }
+ final TenantRevokeAdminRequest request = requestBuilder.build();
+ final OMRequest omRequest = createOMRequest(Type.TenantRevokeAdmin)
+ .setTenantRevokeAdminRequest(request)
+ .build();
+ final OMResponse omResponse = submitRequest(omRequest);
+ handleError(omResponse);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
@Override
public TenantUserInfoValue tenantGetUserInfo(String userPrincipal)
throws IOException {
@@ -957,6 +1029,24 @@ public final class OzoneManagerProtocolClientSideTranslatorPB
}
/**
+ * {@inheritDoc}
+ */
+ @Override
+ public TenantInfoList listTenant() throws IOException {
+
+ final ListTenantRequest request = ListTenantRequest.newBuilder()
+ .build();
+ final OMRequest omRequest = createOMRequest(Type.ListTenant)
+ .setListTenantRequest(request)
+ .build();
+ final OMResponse omResponse = submitRequest(omRequest);
+ final ListTenantResponse resp = handleError(omResponse)
+ .getListTenantResponse();
+
+ return TenantInfoList.fromProtobuf(resp.getTenantInfoList());
+ }
+
+ /**
* Return the proxy object underlying this protocol translator.
*
* @return the proxy object underlying this protocol translator.
diff --git a/hadoop-ozone/dist/src/main/compose/ozonesecure/mockserverInitialization.json b/hadoop-ozone/dist/src/main/compose/ozonesecure/mockserverInitialization.json
index e1142bc..4a54f4c 100644
--- a/hadoop-ozone/dist/src/main/compose/ozonesecure/mockserverInitialization.json
+++ b/hadoop-ozone/dist/src/main/compose/ozonesecure/mockserverInitialization.json
@@ -17,7 +17,23 @@
},
{
"httpRequest": {
- "path": "/service/xusers/secure/groups"
+ "path": "/service/roles/roles"
+ },
+ "httpResponse": {
+ "body": "{id: 222}"
+ }
+ },
+ {
+ "httpRequest": {
+ "path": "/service/roles/roles/name/tenantone-UserRole"
+ },
+ "httpResponse": {
+ "body": "{id: 222, users: []}"
+ }
+ },
+ {
+ "httpRequest": {
+ "path": "/service/roles/roles/222"
},
"httpResponse": {
"body": "{id: 222}"
@@ -33,7 +49,7 @@
},
{
"httpRequest": {
- "path": "/service/public/v2/api/policy/*",
+ "path": "/service/public/v2/api/policy/*"
},
"httpResponse": {
"body": "[{id: 444}]"
diff --git a/hadoop-ozone/dist/src/main/smoketest/security/ozone-secure-tenant.robot b/hadoop-ozone/dist/src/main/smoketest/security/ozone-secure-tenant.robot
index 93ab1f9..82e80e7 100644
--- a/hadoop-ozone/dist/src/main/smoketest/security/ozone-secure-tenant.robot
+++ b/hadoop-ozone/dist/src/main/smoketest/security/ozone-secure-tenant.robot
@@ -33,19 +33,18 @@ Init Ranger MockServer
Secure Tenant Create Tenant Success
# Run Keyword Kinit test user testuser testuser.keytab
Run Keyword Init Ranger MockServer
- ${output} = Execute ozone tenant create finance
- Should contain ${output} Created tenant 'finance'
+ ${output} = Execute ozone tenant create tenantone
+ Should contain ${output} Created tenant 'tenantone'
Secure Tenant Assign User Success
- ${output} = Execute ozone tenant user assign bob@EXAMPLE.COM --tenant=finance
- Should contain ${output} Assigned 'bob@EXAMPLE.COM' to 'finance'
+ ${output} = Execute ozone tenant user assign bob --tenant=tenantone
+ Should contain ${output} Assigned 'bob' to 'tenantone'
Secure Tenant GetUserInfo Success
- ${output} = Execute ozone tenant user info bob@EXAMPLE.COM
- Should contain ${output} Tenant 'finance' with accessId 'finance$bob@EXAMPLE.COM'
+ ${output} = Execute ozone tenant user info bob
+ Should contain ${output} Tenant 'tenantone' with accessId 'tenantone$bob'
Secure Tenant Assign User Failure
- ${rc} ${result} = Run And Return Rc And Output ozone tenant user assign bob@EXAMPLE.COM --tenant=nonexistenttenant
-# Should Be True ${rc} > 0
- Should contain ${result} tenant 'nonexistenttenant' doesn't exist
+ ${rc} ${result} = Run And Return Rc And Output ozone tenant user assign bob --tenant=thistenantdoesnotexist
+ Should contain ${result} Tenant 'thistenantdoesnotexist' doesn't exist
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 aa28900..7532905 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
@@ -99,16 +99,16 @@ public class TestMultiTenantAccessAuthorizerRangerPlugin {
omm.init(conf);
try {
- OzoneTenantGroupPrincipal group1Principal =
- OzoneTenantGroupPrincipal.newAdminGroup("tenant1");
- OzoneTenantGroupPrincipal group2Principal =
- OzoneTenantGroupPrincipal.newUserGroup("tenant1");
- groupIdsCreated.add(omm.createGroup(group1Principal));
- groupIdsCreated.add(omm.createGroup(group2Principal));
+ OzoneTenantRolePrincipal adminRole =
+ OzoneTenantRolePrincipal.getAdminRole("tenant1-AdminRole");
+ OzoneTenantRolePrincipal userRole =
+ OzoneTenantRolePrincipal.getUserRole("tenant1-UserRole");
- BasicUserPrincipal userPrincipal =
- new BasicUserPrincipal("user1Test");
- usersIdsCreated.add(omm.createUser(userPrincipal, groupIdsCreated));
+ BasicUserPrincipal userPrincipal = new BasicUserPrincipal("user1Test");
+ usersIdsCreated.add(
+ omm.assignUser(userPrincipal, userRole.getName(), false));
+ usersIdsCreated.add(
+ omm.assignUser(userPrincipal, adminRole.getName(), true));
AccessPolicy tenant1VolumeAccessPolicy = createVolumeAccessPolicy(
"vol1", "tenant1");
@@ -136,7 +136,7 @@ public class TestMultiTenantAccessAuthorizerRangerPlugin {
omm.deleteUser(id);
}
for (String id : groupIdsCreated) {
- omm.deleteGroup(id);
+ omm.deleteRole(id);
}
}
}
@@ -153,17 +153,17 @@ public class TestMultiTenantAccessAuthorizerRangerPlugin {
try {
Assert.assertTrue(policyIdsCreated.size() == 0);
- OzoneTenantGroupPrincipal group1Principal =
- OzoneTenantGroupPrincipal.newAdminGroup("tenant1");
- OzoneTenantGroupPrincipal group2Principal =
- OzoneTenantGroupPrincipal.newUserGroup("tenant1");
- omm.createGroup(group1Principal);
- groupIdsCreated.add(omm.getGroupId(group1Principal));
- omm.createGroup(group2Principal);
- groupIdsCreated.add(omm.getGroupId(group2Principal));
+ OzoneTenantRolePrincipal group1Principal =
+ OzoneTenantRolePrincipal.getAdminRole("tenant1");
+ OzoneTenantRolePrincipal group2Principal =
+ OzoneTenantRolePrincipal.getUserRole("tenant1");
+ omm.createRole(group1Principal, null);
+ groupIdsCreated.add(omm.getRole(group1Principal));
+ omm.createRole(group2Principal, group1Principal.getName());
+ groupIdsCreated.add(omm.getRole(group2Principal));
userPrincipal = new BasicUserPrincipal("user1Test");
- omm.createUser(userPrincipal, groupIdsCreated);
+ omm.assignUser(userPrincipal, group2Principal.getName(), false);
AccessPolicy tenant1VolumeAccessPolicy = createVolumeAccessPolicy(
"vol1", "tenant1");
@@ -194,17 +194,18 @@ public class TestMultiTenantAccessAuthorizerRangerPlugin {
String userId = omm.getUserId(userPrincipal);
omm.deleteUser(userId);
for (String id : groupIdsCreated) {
- omm.deleteGroup(id);
+ omm.deleteRole(id);
}
}
}
private AccessPolicy createVolumeAccessPolicy(String vol, String tenant)
throws IOException {
- OzoneTenantGroupPrincipal principal =
- OzoneTenantGroupPrincipal.newUserGroup(tenant);
+ OzoneTenantRolePrincipal principal =
+ OzoneTenantRolePrincipal.getUserRole(tenant);
AccessPolicy tenantVolumeAccessPolicy = new RangerAccessPolicy(
- principal.getName() + "VolumeAccess" + vol + "Policy");
+ // principal already contains volume name
+ principal.getName() + "VolumeAccess");
OzoneObjInfo obj = OzoneObjInfo.Builder.newBuilder()
.setResType(VOLUME).setStoreType(OZONE).setVolumeName(vol)
.setBucketName("").setKeyName("").build();
@@ -217,10 +218,11 @@ public class TestMultiTenantAccessAuthorizerRangerPlugin {
private AccessPolicy allowCreateBucketPolicy(String vol, String tenant)
throws IOException {
- OzoneTenantGroupPrincipal principal =
- OzoneTenantGroupPrincipal.newUserGroup(tenant);
+ OzoneTenantRolePrincipal principal =
+ OzoneTenantRolePrincipal.getUserRole(tenant);
AccessPolicy tenantVolumeAccessPolicy = new RangerAccessPolicy(
- principal.getName() + "AllowBucketCreate" + vol + "Policy");
+ // principal already contains volume name
+ principal.getName() + "BucketAccess");
OzoneObjInfo obj = OzoneObjInfo.Builder.newBuilder()
.setResType(BUCKET).setStoreType(OZONE).setVolumeName(vol)
.setBucketName("*").setKeyName("").build();
@@ -228,10 +230,11 @@ public class TestMultiTenantAccessAuthorizerRangerPlugin {
return tenantVolumeAccessPolicy;
}
+ // TODO: REMOVE THIS?
private AccessPolicy allowAccessBucketPolicy(String vol, String bucketName,
String tenant) throws IOException {
- OzoneTenantGroupPrincipal principal =
- OzoneTenantGroupPrincipal.newUserGroup(tenant);
+ OzoneTenantRolePrincipal principal =
+ OzoneTenantRolePrincipal.getUserRole(tenant);
AccessPolicy tenantVolumeAccessPolicy = new RangerAccessPolicy(
principal.getName() + "AllowBucketAccess" + vol + bucketName +
"Policy");
@@ -249,8 +252,8 @@ public class TestMultiTenantAccessAuthorizerRangerPlugin {
private AccessPolicy allowAccessKeyPolicy(String vol, String bucketName,
String tenant) throws IOException {
- OzoneTenantGroupPrincipal principal =
- OzoneTenantGroupPrincipal.newUserGroup(tenant);
+ OzoneTenantRolePrincipal principal =
+ OzoneTenantRolePrincipal.getUserRole(tenant);
AccessPolicy tenantVolumeAccessPolicy = new RangerAccessPolicy(
principal.getName() + "AllowBucketKeyAccess" + vol + bucketName +
"Policy");
diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantVolume.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantVolume.java
index 2bca0aa..2415285 100644
--- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantVolume.java
+++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantVolume.java
@@ -99,7 +99,7 @@ public class TestMultiTenantVolume {
ObjectStore store = getStoreForAccessID(accessID);
store.createTenant(tenant);
- store.assignUserToTenant(principal, tenant, accessID);
+ store.tenantAssignUserAccessId(principal, tenant, accessID);
// S3 volume pointed to by the store should be for the tenant.
Assert.assertEquals(tenant, store.getS3Volume().getName());
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 8fd32ff..8204273 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
@@ -28,6 +28,8 @@ import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.hadoop.ozone.ha.ConfUtils;
import org.apache.hadoop.ozone.om.OMConfigKeys;
import org.apache.hadoop.ozone.om.OMMultiTenantManagerImpl;
+import org.apache.hadoop.ozone.om.helpers.OmVolumeArgs;
+import org.apache.hadoop.ozone.om.multitenant.MultiTenantAccessAuthorizerRangerPlugin;
import org.apache.hadoop.ozone.om.request.s3.tenant.OMAssignUserToTenantRequest;
import org.apache.hadoop.ozone.om.request.s3.tenant.OMTenantCreateRequest;
import org.apache.hadoop.ozone.shell.tenant.TenantShell;
@@ -55,6 +57,9 @@ import java.util.List;
import java.util.UUID;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_RANGER_HTTPS_ADMIN_API_PASSWD;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_RANGER_HTTPS_ADMIN_API_USER;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_HTTPS_ADDRESS_KEY;
import static org.junit.Assert.fail;
/**
@@ -97,6 +102,8 @@ public class TestOzoneTenantShell {
private static String scmId;
private static int numOfOMs;
+ private static final boolean USE_ACTUAL_RANGER = false;
+
/**
* Create a MiniOzoneCluster for testing with using distributed Ozone
* handler type.
@@ -112,8 +119,16 @@ public class TestOzoneTenantShell {
conf = new OzoneConfiguration();
- conf.setBoolean(
- OMMultiTenantManagerImpl.OZONE_OM_TENANT_DEV_SKIP_RANGER, true);
+ if (USE_ACTUAL_RANGER) {
+ conf.set(OZONE_RANGER_HTTPS_ADDRESS_KEY, System.getenv("RANGER_ADDRESS"));
+ conf.set(OZONE_OM_RANGER_HTTPS_ADMIN_API_USER,
+ System.getenv("RANGER_USER"));
+ conf.set(OZONE_OM_RANGER_HTTPS_ADMIN_API_PASSWD,
+ System.getenv("RANGER_PASSWD"));
+ } else {
+ conf.setBoolean(OMMultiTenantManagerImpl.OZONE_OM_TENANT_DEV_SKIP_RANGER,
+ true);
+ }
String path = GenericTestUtils.getTempPath(
TestOzoneTenantShell.class.getSimpleName());
@@ -136,9 +151,9 @@ public class TestOzoneTenantShell {
.setScmId(scmId)
.setOMServiceId(omServiceId)
.setNumOfOzoneManagers(numOfOMs)
+ .withoutDatanodes() // Remove this once we are actually writing data
.build();
cluster.waitForClusterToBeReady();
-// MiniOzoneHAClusterImpl impl = (MiniOzoneOMHAClusterImpl) cluster;
}
/**
@@ -159,6 +174,14 @@ public class TestOzoneTenantShell {
public void setup() throws UnsupportedEncodingException {
System.setOut(new PrintStream(out, false, UTF_8.name()));
System.setErr(new PrintStream(err, false, UTF_8.name()));
+
+ // Suppress OMNotLeaderException in the log
+ GenericTestUtils.setLogLevel(RetryInvocationHandler.LOG, Level.WARN);
+ // Enable debug logging for interested classes
+ GenericTestUtils.setLogLevel(OMTenantCreateRequest.LOG, Level.DEBUG);
+ GenericTestUtils.setLogLevel(OMAssignUserToTenantRequest.LOG, Level.DEBUG);
+ GenericTestUtils.setLogLevel(MultiTenantAccessAuthorizerRangerPlugin.LOG,
+ Level.DEBUG);
}
@After
@@ -304,35 +327,81 @@ public class TestOzoneTenantShell {
if (exactMatch) {
Assert.assertEquals(stringToMatch, str);
} else {
- Assert.assertTrue(str.contains(stringToMatch));
+ Assert.assertTrue(str, str.contains(stringToMatch));
}
}
- /**
- * Test tenant create, assign user and get user info.
- */
@Test
- public void testOzoneTenantCreateAssignInfo() throws IOException {
+ public void testAssignAdmin() throws IOException {
- // Suppress OMNotLeaderException in the log
- GenericTestUtils.setLogLevel(RetryInvocationHandler.LOG, Level.WARN);
+ final String tenantName = "devaa";
+ final String userName = "alice";
- GenericTestUtils.setLogLevel(OMTenantCreateRequest.LOG, Level.DEBUG);
- GenericTestUtils.setLogLevel(OMAssignUserToTenantRequest.LOG, Level.DEBUG);
+ executeHA(tenantShell, new String[] {"create", tenantName});
+ checkOutput(out, "Created tenant", false);
+ checkOutput(err, "", true);
+
+ // Loop assign-revoke 3 times
+ for (int i = 0; i < 3; i++) {
+ executeHA(tenantShell, new String[] {
+ "user", "assign", userName, "--tenant=" + tenantName});
+ checkOutput(out, "export AWS_ACCESS_KEY_ID=", false);
+ checkOutput(err, "Assigned", false);
+
+ executeHA(tenantShell, new String[] {"user", "assign-admin",
+ tenantName + "$" + userName, "--tenant=" + tenantName,
+ "--delegated=true"});
+ checkOutput(out, "", true);
+ checkOutput(err, "Assigned admin", false);
+
+ // Clean up
+ executeHA(tenantShell, new String[] {
+ "user", "revoke-admin", tenantName + "$" + userName,
+ "--tenant=" + tenantName});
+ checkOutput(out, "", true);
+ checkOutput(err, "Revoked admin role of", false);
+
+ executeHA(tenantShell, new String[] {
+ "user", "revoke", tenantName + "$" + userName});
+ checkOutput(out, "", true);
+ checkOutput(err, "Revoked accessId", false);
+ }
+
+ // TODO: delete tenant is not implemented yet
+ executeHA(tenantShell, new String[] {"delete", tenantName});
+ }
+
+ /**
+ * Test tenant create, assign user, get user info, assign admin, revoke admin
+ * and revoke user flow.
+ */
+ @Test
+ public void testOzoneTenantBasicOperations() throws IOException {
List<String> lines = FileUtils.readLines(AUDIT_LOG_FILE, (String)null);
Assert.assertEquals(0, lines.size());
+ executeHA(tenantShell, new String[] {"list"});
+ checkOutput(out, "", true);
+ checkOutput(err, "", true);
+
// Create tenants
// Equivalent to `ozone tenant create finance`
executeHA(tenantShell, new String[] {"create", "finance"});
checkOutput(out, "Created tenant 'finance'.\n", true);
checkOutput(err, "", true);
- lines = FileUtils.readLines(AUDIT_LOG_FILE, (String)null);
- checkOutput(lines.get(lines.size() - 1), "ret=SUCCESS", false);
+// lines = FileUtils.readLines(AUDIT_LOG_FILE, (String)null);
+ // FIXME: The check below is unstable.
+ // Occasionally lines.size() == 0 leads to ArrayIndexOutOfBoundsException
+ // Likely due to audit log not flushed in time at time of check.
+// checkOutput(lines.get(lines.size() - 1), "ret=SUCCESS", false);
+
+ // Check volume creation
+ OmVolumeArgs volArgs = cluster.getOzoneManager().getVolumeInfo("finance");
+ Assert.assertEquals("finance", volArgs.getVolume());
- // Creating the tenant with the same name again should result in failure
+ // Creating the tenant with the same name again should fail
executeHA(tenantShell, new String[] {"create", "finance"});
checkOutput(out, "", true);
checkOutput(err, "Failed to create tenant 'finance':"
@@ -346,38 +415,112 @@ public class TestOzoneTenantShell {
checkOutput(out, "Created tenant 'dev'.\n", true);
checkOutput(err, "", true);
- // Assign user
- // Equivalent to `ozone tenant user assign bob@EXAMPLE.COM --tenant=finance`
+ executeHA(tenantShell, new String[] {"ls"});
+ checkOutput(out, "dev\nfinance\nresearch\n", true);
+ checkOutput(err, "", true);
+
+ executeHA(tenantShell, new String[] {"list", "--long", "--header"});
+ // Not checking the entire output here yet
+ checkOutput(out, "Policy", false);
+ checkOutput(err, "", true);
+
+ // Assign user accessId
+ // Equivalent to `ozone tenant user assign bob --tenant=finance`
executeHA(tenantShell, new String[] {
- "user", "assign", "bob@EXAMPLE.COM", "--tenant=finance"});
- checkOutput(out, "export AWS_ACCESS_KEY_ID='finance$bob@EXAMPLE.COM'\n"
+ "user", "assign", "bob", "--tenant=finance"});
+ checkOutput(out, "export AWS_ACCESS_KEY_ID='finance$bob'\n"
+ "export AWS_SECRET_ACCESS_KEY='", false);
- checkOutput(err, "Assigned 'bob@EXAMPLE.COM' to 'finance' with accessId"
- + " 'finance$bob@EXAMPLE.COM'.\n", true);
+ checkOutput(err, "Assigned 'bob' to 'finance' with accessId"
+ + " 'finance$bob'.\n", true);
executeHA(tenantShell, new String[] {
- "user", "assign", "bob@EXAMPLE.COM", "--tenant=research"});
- checkOutput(out, "export AWS_ACCESS_KEY_ID='research$bob@EXAMPLE.COM'\n"
+ "user", "assign", "bob", "--tenant=research"});
+ checkOutput(out, "export AWS_ACCESS_KEY_ID='research$bob'\n"
+ "export AWS_SECRET_ACCESS_KEY='", false);
- checkOutput(err, "Assigned 'bob@EXAMPLE.COM' to 'research' with accessId"
- + " 'research$bob@EXAMPLE.COM'.\n", true);
+ checkOutput(err, "Assigned 'bob' to 'research' with accessId"
+ + " 'research$bob'.\n", true);
executeHA(tenantShell, new String[] {
- "user", "assign", "bob@EXAMPLE.COM", "--tenant=dev"});
- checkOutput(out, "export AWS_ACCESS_KEY_ID='dev$bob@EXAMPLE.COM'\n"
+ "user", "assign", "bob", "--tenant=dev"});
+ checkOutput(out, "export AWS_ACCESS_KEY_ID='dev$bob'\n"
+ "export AWS_SECRET_ACCESS_KEY='", false);
- checkOutput(err, "Assigned 'bob@EXAMPLE.COM' to 'dev' with accessId"
- + " 'dev$bob@EXAMPLE.COM'.\n", true);
+ checkOutput(err, "Assigned 'bob' to 'dev' with accessId"
+ + " 'dev$bob'.\n", true);
// Get user info
- // Equivalent to `ozone tenant user info bob@EXAMPLE.COM`
+ // Equivalent to `ozone tenant user info bob`
+ executeHA(tenantShell, new String[] {
+ "user", "info", "bob"});
+ checkOutput(out, "User 'bob' is assigned to:\n"
+ + "- Tenant 'research' with accessId 'research$bob'\n"
+ + "- Tenant 'finance' with accessId 'finance$bob'\n"
+ + "- Tenant 'dev' with accessId 'dev$bob'\n", true);
+ checkOutput(err, "", true);
+
+ // Assign admin
+ executeHA(tenantShell, new String[] {
+ "user", "assign-admin", "dev$bob", "--tenant=dev"});
+ checkOutput(out, "", true);
+ checkOutput(err,
+ "Assigned admin to 'dev$bob' in tenant 'dev'\n", true);
+
+ executeHA(tenantShell, new String[] {
+ "user", "info", "bob"});
+ checkOutput(out, "Tenant 'dev' delegated admin with accessId", false);
+ checkOutput(err, "", true);
+
+ // Revoke admin
+ executeHA(tenantShell, new String[] {
+ "user", "revoke-admin", "dev$bob", "--tenant=dev"});
+ checkOutput(out, "", true);
+ checkOutput(err, "Revoked admin role of 'dev$bob' "
+ + "from tenant 'dev'\n", true);
+
+ executeHA(tenantShell, new String[] {
+ "user", "info", "bob"});
+ checkOutput(out, "User 'bob' is assigned to:\n"
+ + "- Tenant 'research' with accessId 'research$bob'\n"
+ + "- Tenant 'finance' with accessId 'finance$bob'\n"
+ + "- Tenant 'dev' with accessId 'dev$bob'\n", true);
+ checkOutput(err, "", true);
+
+ // Revoke user accessId
+ executeHA(tenantShell, new String[] {
+ "user", "revoke", "research$bob"});
+ checkOutput(out, "", true);
+ checkOutput(err, "Revoked accessId 'research$bob'.\n", true);
+
executeHA(tenantShell, new String[] {
- "user", "info", "bob@EXAMPLE.COM"});
- checkOutput(out, "User 'bob@EXAMPLE.COM' is assigned to:\n"
- + "- Tenant 'finance' with accessId 'finance$bob@EXAMPLE.COM'\n"
- + "- Tenant 'research' with accessId 'research$bob@EXAMPLE.COM'\n"
- + "- Tenant 'dev' with accessId 'dev$bob@EXAMPLE.COM'\n\n", true);
+ "user", "info", "bob"});
+ checkOutput(out, "User 'bob' is assigned to:\n"
+ + "- Tenant 'finance' with accessId 'finance$bob'\n"
+ + "- Tenant 'dev' with accessId 'dev$bob'\n", true);
checkOutput(err, "", true);
+
+ // Assign user again
+ executeHA(tenantShell, new String[] {
+ "user", "assign", "bob", "--tenant=research"});
+ checkOutput(out, "export AWS_ACCESS_KEY_ID='research$bob'\n"
+ + "export AWS_SECRET_ACCESS_KEY='", false);
+ checkOutput(err, "Assigned 'bob' to 'research' with accessId"
+ + " 'research$bob'.\n", true);
+
+ // Attempt to assign the user to the same tenant under another accessId,
+ // should fail.
+ executeHA(tenantShell, new String[] {
+ "user", "assign", "bob", "--tenant=research",
+ "--accessId=research$bob42"});
+ checkOutput(out, "", false);
+ checkOutput(err, "Failed to assign 'bob' to 'research': "
+ + "The same user is not allowed to be assigned to the same tenant "
+ + "more than once. User 'bob' is already assigned to tenant 'research' "
+ + "with accessId 'research$bob'.\n", true);
+
+ // Clean up
+ executeHA(tenantShell, new String[] {
+ "user", "revoke", "research$bob"});
+ checkOutput(out, "", true);
+ checkOutput(err, "Revoked accessId", false);
}
}
diff --git a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
index 296b4ca..6867247 100644
--- a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
+++ b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
@@ -106,11 +106,16 @@ enum Type {
CreateTenant = 95;
ModifyTenant = 96;
DeleteTenant = 97;
+ ListTenant = 103; // TODO: Renumber this when rebasing
TenantGetUserInfo = 98;
- AssignUserToTenant = 99;
- RevokeUserAccessToTenant = 100;
- GetS3Volume = 101;
+ TenantAssignUserAccessId = 99;
+ TenantRevokeUserAccessId = 100;
+
+ TenantAssignAdmin = 101;
+ TenantRevokeAdmin = 102;
+
+ GetS3Volume = 104;
}
message OMRequest {
@@ -194,14 +199,19 @@ message OMRequest {
optional PurgePathsRequest purgePathsRequest = 94;
- optional CreateTenantRequest createTenantRequest = 95;
- optional ModifyTenantRequest modifyTenantRequest = 96;
- optional DeleteTenantRequest deleteTenantRequest = 97;
+ optional CreateTenantRequest CreateTenantRequest = 95;
+ optional ModifyTenantRequest ModifyTenantRequest = 96;
+ optional DeleteTenantRequest DeleteTenantRequest = 97;
+ optional ListTenantRequest ListTenantRequest = 103; // TODO: Renumber this when rebasing
optional TenantGetUserInfoRequest TenantGetUserInfoRequest = 98;
- optional AssignUserToTenantRequest AssignUserToTenantRequest = 99;
- optional RevokeUserAccessToTenantRequest RevokeUserAccessToTenantRequest = 100;
- optional GetS3VolumeRequest getS3VolumeRequest = 101;
+ optional TenantAssignUserAccessIdRequest TenantAssignUserAccessIdRequest= 99;
+ optional TenantRevokeUserAccessIdRequest TenantRevokeUserAccessIdRequest= 100;
+
+ optional TenantAssignAdminRequest TenantAssignAdminRequest = 101;
+ optional TenantRevokeAdminRequest TenantRevokeAdminRequest = 102;
+
+ optional GetS3VolumeRequest getS3VolumeRequest = 104;
}
message OMResponse {
@@ -280,14 +290,19 @@ message OMResponse {
optional PurgePathsResponse purgePathsResponse = 93;
// Skipped 94 to align with OMRequest
- optional CreateTenantResponse createTenantResponse = 95;
- optional ModifyTenantResponse modifyTenantResponse = 96;
- optional DeleteTenantResponse deleteTenantResponse = 97;
+ optional CreateTenantResponse CreateTenantResponse = 95;
+ optional ModifyTenantResponse ModifyTenantResponse = 96;
+ optional DeleteTenantResponse DeleteTenantResponse = 97;
+ optional ListTenantResponse ListTenantResponse = 103; // TODO: Renumber this when rebasing
optional TenantGetUserInfoResponse TenantGetUserInfoResponse = 98;
- optional AssignUserToTenantResponse AssignUserToTenantResponse = 99;
- optional RevokeUserAccessToTenantResponse RevokeUserAccessToTenantResponse = 100;
- optional GetS3VolumeResponse getS3VolumeResponse = 101;
+ optional TenantAssignUserAccessIdResponse TenantAssignUserAccessIdResponse= 99;
+ optional TenantRevokeUserAccessIdResponse TenantRevokeUserAccessIdResponse= 100;
+
+ optional TenantAssignAdminResponse TenantAssignAdminResponse = 101;
+ optional TenantRevokeAdminResponse TenantRevokeAdminResponse = 102;
+
+ optional GetS3VolumeResponse getS3VolumeResponse = 104;
}
enum Status {
@@ -387,9 +402,10 @@ enum Status {
TENANT_ALREADY_EXISTS = 76;
INVALID_TENANT_NAME = 77;
- TENANT_USER_NOT_FOUND = 78;
- TENANT_USER_ALREADY_EXISTS = 79;
+ TENANT_USER_ACCESSID_NOT_FOUND = 78;
+ TENANT_USER_ACCESSID_ALREADY_EXISTS = 79; // TODO: Remove if not used
INVALID_TENANT_USER_NAME = 80;
+ INVALID_ACCESSID = 81;
}
/**
@@ -1362,25 +1378,45 @@ message GetS3SecretResponse {
required S3Secret s3Secret = 2;
}
-message TenantUserInfo {
- optional string userPrincipal = 1;
- repeated TenantAccessIdInfo accessIdInfo = 2;
+message TenantInfo {
+ optional string tenantName = 1;
+ optional string bucketNamespaceName = 2;
+ optional string accountNamespaceName = 3;
+ optional string userPolicyGroupName = 4;
+ optional string bucketPolicyGroupName = 5;
}
-message TenantAccessIdInfo {
- optional string accessId = 1;
- optional string tenantName = 2;
+message ListTenantRequest {
+
+}
+
+message ListTenantResponse {
+ optional bool success = 1;
+ repeated TenantInfo tenantInfo = 2;
}
message TenantGetUserInfoRequest {
- optional string userPrincipal = 1;
+ optional string userPrincipal = 1;
}
message TenantGetUserInfoResponse {
- optional bool success = 1;
- optional TenantUserInfo tenantUserInfo = 2;
+ optional bool success = 1;
+ optional TenantUserInfo tenantUserInfo = 2;
}
+message TenantUserInfo {
+ optional string userPrincipal = 1;
+ repeated TenantAccessIdInfo accessIdInfo = 2;
+}
+
+message TenantAccessIdInfo {
+ optional string accessId = 1;
+ optional string tenantName = 2;
+ optional bool isAdmin = 3;
+ optional bool isDelegatedAdmin = 4;
+}
+
+
message LayoutVersion {
required uint64 version = 1;
}
@@ -1402,13 +1438,24 @@ message DeleteTenantRequest {
optional string tenantName = 1;
}
-message AssignUserToTenantRequest {
+message TenantAssignUserAccessIdRequest {
optional string tenantUsername = 1;
optional string tenantName = 2;
optional string accessId = 3;
}
-message RevokeUserAccessToTenantRequest {
+message TenantRevokeUserAccessIdRequest {
+ optional string accessId = 1;
+ optional string tenantName = 2;
+}
+
+message TenantAssignAdminRequest {
+ optional string accessId = 1;
+ optional string tenantName = 2;
+ optional bool delegated = 3;
+}
+
+message TenantRevokeAdminRequest {
optional string accessId = 1;
optional string tenantName = 2;
}
@@ -1429,12 +1476,20 @@ message DeleteTenantResponse {
optional bool success = 1;
}
-message AssignUserToTenantResponse {
+message TenantAssignUserAccessIdResponse {
optional bool success = 1;
optional S3Secret s3Secret = 2;
}
-message RevokeUserAccessToTenantResponse {
+message TenantRevokeUserAccessIdResponse {
+ optional bool success = 1;
+}
+
+message TenantAssignAdminResponse {
+ optional bool success = 1;
+}
+
+message TenantRevokeAdminResponse {
optional bool success = 1;
}
diff --git a/hadoop-ozone/interface-storage/src/main/java/org/apache/hadoop/ozone/om/OMMetadataManager.java b/hadoop-ozone/interface-storage/src/main/java/org/apache/hadoop/ozone/om/OMMetadataManager.java
index 760cc00..74c790d 100644
--- a/hadoop-ozone/interface-storage/src/main/java/org/apache/hadoop/ozone/om/OMMetadataManager.java
+++ b/hadoop-ozone/interface-storage/src/main/java/org/apache/hadoop/ozone/om/OMMetadataManager.java
@@ -357,8 +357,6 @@ public interface OMMetadataManager extends DBStoreHAManager {
Table<String, TransactionInfo> getTransactionInfoTable();
- Table<String, String> getTenantUserTable();
-
Table<String, OmDBAccessIdInfo> getTenantAccessIdTable();
Table<String, OmDBKerberosPrincipalInfo> getPrincipalToAccessIdsTable();
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 6621417..5363e3b 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
@@ -111,14 +111,14 @@ public interface OMMultiTenantManager {
/**
* Creates a new user that exists for S3 API access to Ozone.
- * @param tenantName
* @param principal
+ * @param tenantName
* @param accessID
* @return Unique UserID.
* @throws IOException if there is any error condition detected.
*/
String assignUserToTenant(BasicUserPrincipal principal, String tenantName,
- String accessID);
+ String accessID) throws IOException;
/**
* Given a user, destroys all state associated with that user.
@@ -127,7 +127,7 @@ public interface OMMultiTenantManager {
* @return
* @throws IOException
*/
- void destroyUser(String accessID);
+ void revokeUserAccessId(String accessID) throws IOException;
/**
@@ -177,8 +177,9 @@ public interface OMMultiTenantManager {
/**
* Given a user, make him an admin of the corresponding Tenant.
* @param accessID
+ * @param delegated
*/
- void assignTenantAdminRole(String accessID) throws IOException;
+ void assignTenantAdmin(String accessID, boolean delegated) throws IOException;
/**
* Given a user, remove him as admin of the corresponding Tenant.
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 d7265dc..b82052e 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
@@ -18,6 +18,7 @@
package org.apache.hadoop.ozone.om;
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;
import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.LIST;
import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.NONE;
@@ -29,7 +30,7 @@ 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.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -48,7 +49,8 @@ import org.apache.hadoop.ozone.om.multitenant.CephCompatibleTenantImpl;
import org.apache.hadoop.ozone.om.multitenant.MultiTenantAccessAuthorizer;
import org.apache.hadoop.ozone.om.multitenant.MultiTenantAccessAuthorizerDummyPlugin;
import org.apache.hadoop.ozone.om.multitenant.MultiTenantAccessAuthorizerRangerPlugin;
-import org.apache.hadoop.ozone.om.multitenant.OzoneTenantGroupPrincipal;
+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.security.acl.IAccessAuthorizer.ACLType;
@@ -215,42 +217,45 @@ public class OMMultiTenantManagerImpl implements OMMultiTenantManager {
// TODO : Make it an idempotent operation. If any ranger state creation
// fails because it already exists, Ignore it.
- OzoneTenantGroupPrincipal allTenantUsers =
- OzoneTenantGroupPrincipal.newUserGroup(tenantID);
- String allTenantUsersGroupID = authorizer.createGroup(allTenantUsers);
- tenant.addTenantAccessGroup(allTenantUsersGroupID);
+ // Create admin role first
+ final OzoneTenantRolePrincipal adminRole =
+ OzoneTenantRolePrincipal.getAdminRole(tenantID);
+ String adminRoleId = authorizer.createRole(adminRole, null);
+ tenant.addTenantAccessRole(adminRoleId);
- OzoneTenantGroupPrincipal allTenantAdmins =
- OzoneTenantGroupPrincipal.newAdminGroup(tenantID);
- String allTenantAdminsGroupID = authorizer.createGroup(allTenantAdmins);
- tenant.addTenantAccessGroup(allTenantAdminsGroupID);
+ // Then create user role, and add admin role as its delegated admin
+ final OzoneTenantRolePrincipal userRole =
+ OzoneTenantRolePrincipal.getUserRole(tenantID);
+ String userRoleId = authorizer.createRole(userRole, adminRole.getName());
+ tenant.addTenantAccessRole(userRoleId);
- List<String> allTenantGroups = new ArrayList<>();
- allTenantGroups.add(allTenantUsers.toString());
- allTenantGroups.add(allTenantAdmins.toString());
- inMemoryTenantToTenantGroups.put(tenantID, allTenantGroups);
+ 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()) {
String volumeName = volume.getVolumeName();
+
// Allow Volume List access
- AccessPolicy tenantVolumeAccessPolicy = createVolumeAccessPolicy(
- volumeName, allTenantUsers);
+ AccessPolicy tenantVolumeAccessPolicy = newDefaultVolumeAccessPolicy(
+ volumeName, userRole, adminRole);
tenantVolumeAccessPolicy.setPolicyID(
authorizer.createAccessPolicy(tenantVolumeAccessPolicy));
tenant.addTenantAccessPolicy(tenantVolumeAccessPolicy);
// Allow Bucket Create within Volume
- AccessPolicy tenantBucketCreatePolicy = allowCreateBucketPolicy(
- volumeName, allTenantUsers);
+ AccessPolicy tenantBucketCreatePolicy =
+ newDefaultBucketAccessPolicy(volumeName, userRole);
tenantBucketCreatePolicy.setPolicyID(
authorizer.createAccessPolicy(tenantBucketCreatePolicy));
tenant.addTenantAccessPolicy(tenantBucketCreatePolicy);
}
inMemoryTenantToPolicyNameListMap.put(tenantID,
- tenant.getTenantAccessPolicies().stream().map(e->e.getPolicyName())
- .collect(Collectors.toList()));
+ tenant.getTenantAccessPolicies().stream().map(
+ AccessPolicy::getPolicyName).collect(Collectors.toList()));
} catch (Exception e) {
try {
destroyTenant(tenant);
@@ -293,8 +298,8 @@ public class OMMultiTenantManagerImpl implements OMMultiTenantManager {
for (AccessPolicy policy : tenant.getTenantAccessPolicies()) {
authorizer.deletePolicybyId(policy.getPolicyID());
}
- for (String groupID : tenant.getTenantGroups()) {
- authorizer.deleteGroup(groupID);
+ for (String roleId : tenant.getTenantRoles()) {
+ authorizer.deleteRole(roleId);
}
inMemoryTenantNameToTenantInfoMap.remove(tenant.getTenantId());
@@ -331,10 +336,11 @@ public class OMMultiTenantManagerImpl implements OMMultiTenantManager {
* @param tenantName
* @param accessID
* @return Tenant, or null on error
+ * @throws IOException
*/
@Override
public String assignUserToTenant(BasicUserPrincipal principal,
- String tenantName, String accessID) {
+ String tenantName, String accessID) throws IOException {
try {
controlPathLock.writeLock().lock();
Tenant tenant = getTenantInfo(tenantName);
@@ -343,29 +349,25 @@ public class OMMultiTenantManagerImpl implements OMMultiTenantManager {
tenantName);
return null;
}
- final OzoneTenantGroupPrincipal groupTenantAllUsers =
- OzoneTenantGroupPrincipal.newUserGroup(tenantName);
- String idGroupTenantAllUsers = authorizer.getGroupId(groupTenantAllUsers);
- List<String> userGroupIDs = new ArrayList<>();
- userGroupIDs.add(idGroupTenantAllUsers);
-
- String userID = authorizer.createUser(principal, userGroupIDs);
+ 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, userGroupIDs);
+// inMemoryAccessIDToListOfGroupsMap.put(accessID, userRoleIds);
- return userID;
- } catch (Exception e) {
- destroyUser(accessID);
- LOG.error(e.getMessage());
- return null;
+ return roleId;
+ } catch (IOException e) {
+ revokeUserAccessId(accessID);
+ throw e;
} finally {
controlPathLock.writeLock().unlock();
}
}
@Override
- public void destroyUser(String accessID) {
+ public void revokeUserAccessId(String accessID) throws IOException {
try {
controlPathLock.writeLock().lock();
String tenantName = getTenantForAccessID(accessID);
@@ -379,8 +381,6 @@ public class OMMultiTenantManagerImpl implements OMMultiTenantManager {
inMemoryAccessIDToTenantNameMap.remove(accessID);
inMemoryAccessIDToListOfGroupsMap.remove(accessID);
- } catch (Exception e) {
- LOG.error(e.getMessage());
} finally {
controlPathLock.writeLock().unlock();
}
@@ -438,14 +438,35 @@ public class OMMultiTenantManagerImpl implements OMMultiTenantManager {
}
@Override
- public void assignTenantAdminRole(String accessID)
+ public void assignTenantAdmin(String accessID, boolean delegated)
throws IOException {
-
+ try {
+ controlPathLock.writeLock().lock();
+ // tenantId (tenant name) is necessary to retrieve role name
+ final String tenantId = getTenantForAccessID(accessID);
+ assert(tenantId != null);
+
+ final OzoneTenantRolePrincipal existingAdminRole =
+ OzoneTenantRolePrincipal.getAdminRole(tenantId);
+ final String roleJsonStr = authorizer.getRole(existingAdminRole);
+ final String userPrincipal = getUserNameGivenAccessId(accessID);
+ // Add user principal (not accessId!) to the role
+ final String roleId = authorizer.assignUser(
+ new BasicUserPrincipal(userPrincipal), roleJsonStr, delegated);
+ assert(roleId != null);
+
+ // TODO: update some in-memory mappings?
+
+ } catch (IOException e) {
+ revokeTenantAdmin(accessID);
+ throw e;
+ } finally {
+ controlPathLock.writeLock().unlock();
+ }
}
@Override
- public void revokeTenantAdmin(String accessID)
- throws IOException {
+ public void revokeTenantAdmin(String accessID) throws IOException {
}
@@ -509,34 +530,58 @@ public class OMMultiTenantManagerImpl implements OMMultiTenantManager {
}
- private AccessPolicy createVolumeAccessPolicy(String vol,
- OzoneTenantGroupPrincipal principal) throws IOException {
- AccessPolicy tenantVolumeAccessPolicy = new RangerAccessPolicy(
- principal.getName() + "VolumeAccess" + vol + "Policy");
+ private AccessPolicy newDefaultVolumeAccessPolicy(String volumeName,
+ OzoneTenantRolePrincipal userPrinc, OzoneTenantRolePrincipal adminPrinc)
+ throws IOException {
+
+ AccessPolicy policy = new RangerAccessPolicy(
+ // principal already contains volume name
+ volumeName + " - VolumeAccess");
OzoneObjInfo obj = OzoneObjInfo.Builder.newBuilder()
- .setResType(VOLUME).setStoreType(OZONE).setVolumeName(vol)
+ .setResType(VOLUME).setStoreType(OZONE).setVolumeName(volumeName)
.setBucketName("").setKeyName("").build();
- tenantVolumeAccessPolicy.addAccessPolicyElem(obj, principal, READ, ALLOW);
- tenantVolumeAccessPolicy.addAccessPolicyElem(obj, principal, LIST, ALLOW);
- tenantVolumeAccessPolicy.addAccessPolicyElem(obj, principal,
- READ_ACL, ALLOW);
- return tenantVolumeAccessPolicy;
- }
-
- private AccessPolicy allowCreateBucketPolicy(String vol,
- OzoneTenantGroupPrincipal principal) throws IOException {
- AccessPolicy tenantVolumeAccessPolicy = new RangerAccessPolicy(
- principal.getName() + "AllowBucketCreate" + vol + "Policy");
+ // Tenant users have READ, LIST and READ_ACL access on the volume
+ policy.addAccessPolicyElem(obj, userPrinc, READ, ALLOW);
+ policy.addAccessPolicyElem(obj, userPrinc, LIST, ALLOW);
+ policy.addAccessPolicyElem(obj, userPrinc, READ_ACL, ALLOW);
+ // Tenant admins have ALL access on the volume
+ policy.addAccessPolicyElem(obj, adminPrinc, ALL, ALLOW);
+ return policy;
+ }
+
+ private AccessPolicy newDefaultBucketAccessPolicy(String volumeName,
+ OzoneTenantRolePrincipal userPrinc) throws IOException {
+ AccessPolicy policy = new RangerAccessPolicy(
+ // principal already contains volume name
+ volumeName + " - BucketAccess");
OzoneObjInfo obj = OzoneObjInfo.Builder.newBuilder()
- .setResType(BUCKET).setStoreType(OZONE).setVolumeName(vol)
+ .setResType(BUCKET).setStoreType(OZONE).setVolumeName(volumeName)
.setBucketName("*").setKeyName("").build();
- tenantVolumeAccessPolicy.addAccessPolicyElem(obj, principal, CREATE, ALLOW);
- return tenantVolumeAccessPolicy;
+ // Tenant users have permission to CREATE buckets
+ policy.addAccessPolicyElem(obj, userPrinc, CREATE, ALLOW);
+ // Bucket owner have ALL access on their own buckets. TODO: Tentative
+ policy.addAccessPolicyElem(obj, new OzoneOwnerPrincipal(), ALL, ALLOW);
+ return policy;
+ }
+
+ // TODO: Fine-tune this once we have bucket ownership.
+ private AccessPolicy newDefaultKeyAccessPolicy(String volumeName,
+ String bucketName) throws IOException {
+ AccessPolicy policy = new RangerAccessPolicy(
+ // principal already contains volume name
+ volumeName + " - KeyAccess");
+ // TODO: Double check the policy
+ OzoneObjInfo obj = OzoneObjInfo.Builder.newBuilder()
+ .setResType(KEY).setStoreType(OZONE).setVolumeName(volumeName)
+ .setBucketName("*").setKeyName("*").build();
+ // Bucket owners should have ALL permission on their keys
+ policy.addAccessPolicyElem(obj, new OzoneOwnerPrincipal(), ALL, ALLOW);
+ return policy;
}
private AccessPolicy allowAccessBucketPolicy(String vol, String bucketName,
- OzoneTenantGroupPrincipal principal) throws IOException {
- AccessPolicy tenantVolumeAccessPolicy = new RangerAccessPolicy(
+ OzoneTenantRolePrincipal principal) throws IOException {
+ AccessPolicy policy = new RangerAccessPolicy(
principal.getName() + "AllowBucketAccess" + vol + bucketName +
"Policy");
OzoneObjInfo obj = OzoneObjInfo.Builder.newBuilder()
@@ -544,16 +589,16 @@ public class OMMultiTenantManagerImpl implements OMMultiTenantManager {
.setBucketName(bucketName).setKeyName("*").build();
for (ACLType acl : ACLType.values()) {
if (acl != NONE) {
- tenantVolumeAccessPolicy.addAccessPolicyElem(obj, principal, acl,
+ policy.addAccessPolicyElem(obj, principal, acl,
ALLOW);
}
}
- return tenantVolumeAccessPolicy;
+ return policy;
}
private AccessPolicy allowAccessKeyPolicy(String vol, String bucketName,
- OzoneTenantGroupPrincipal principal) throws IOException {
- AccessPolicy tenantVolumeAccessPolicy = new RangerAccessPolicy(
+ OzoneTenantRolePrincipal principal) throws IOException {
+ AccessPolicy policy = new RangerAccessPolicy(
principal.getName() + "AllowBucketKeyAccess" + vol + bucketName +
"Policy");
OzoneObjInfo obj = OzoneObjInfo.Builder.newBuilder()
@@ -561,11 +606,10 @@ public class OMMultiTenantManagerImpl implements OMMultiTenantManager {
.setBucketName(bucketName).setKeyName("*").build();
for (ACLType acl :ACLType.values()) {
if (acl != NONE) {
- tenantVolumeAccessPolicy.addAccessPolicyElem(obj, principal, acl,
- ALLOW);
+ policy.addAccessPolicyElem(obj, principal, acl, ALLOW);
}
}
- return tenantVolumeAccessPolicy;
+ return policy;
}
public OzoneConfiguration getConf() {
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java
index 3e0fe6c..5f1a43a 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java
@@ -193,8 +193,7 @@ public class OmMetadataManagerImpl implements OMMetadataManager {
"transactionInfoTable";
public static final String META_TABLE = "metaTable";
- // Tables for S3 multi-tenancy
- public static final String TENANT_USER_TABLE = "tenantUserTable";
+ // Tables for multi-tenancy
public static final String TENANT_ACCESS_ID_TABLE = "tenantAccessIdTable";
public static final String PRINCIPAL_TO_ACCESS_IDS_TABLE =
"principalToAccessIdsTable";
@@ -220,7 +219,6 @@ public class OmMetadataManagerImpl implements OMMetadataManager {
DELETED_DIR_TABLE,
OPEN_FILE_TABLE,
META_TABLE,
- TENANT_USER_TABLE,
TENANT_ACCESS_ID_TABLE,
PRINCIPAL_TO_ACCESS_IDS_TABLE,
TENANT_STATE_TABLE,
@@ -250,8 +248,7 @@ public class OmMetadataManagerImpl implements OMMetadataManager {
private Table transactionInfoTable;
private Table metaTable;
- // Tables for S3 multi-tenancy
- private Table tenantUserTable;
+ // Tables for multi-tenancy
private Table tenantAccessIdTable;
private Table principalToAccessIdsTable;
private Table tenantStateTable;
@@ -463,7 +460,6 @@ public class OmMetadataManagerImpl implements OMMetadataManager {
.addTable(DELETED_DIR_TABLE)
.addTable(TRANSACTION_INFO_TABLE)
.addTable(META_TABLE)
- .addTable(TENANT_USER_TABLE)
.addTable(TENANT_ACCESS_ID_TABLE)
.addTable(PRINCIPAL_TO_ACCESS_IDS_TABLE)
.addTable(TENANT_STATE_TABLE)
@@ -562,12 +558,6 @@ public class OmMetadataManagerImpl implements OMMetadataManager {
metaTable = this.store.getTable(META_TABLE, String.class, String.class);
checkTableStatus(metaTable, META_TABLE);
- // tenant user name -> tenant name string
- // TODO: Unused. Remove.
- tenantUserTable = this.store.getTable(TENANT_USER_TABLE,
- String.class, String.class);
- checkTableStatus(tenantUserTable, TENANT_USER_TABLE);
-
// accessId -> OmDBAccessIdInfo (tenantId, secret, Kerberos principal)
tenantAccessIdTable = this.store.getTable(TENANT_ACCESS_ID_TABLE,
String.class, OmDBAccessIdInfo.class);
@@ -1338,11 +1328,6 @@ public class OmMetadataManagerImpl implements OMMetadataManager {
}
@Override
- public Table<String, String> getTenantUserTable() {
- return tenantUserTable;
- }
-
- @Override
public Table<String, OmDBAccessIdInfo> getTenantAccessIdTable() {
return tenantAccessIdTable;
}
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 cddcb0d..114bb20 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
@@ -119,6 +119,7 @@ import org.apache.hadoop.ozone.om.helpers.OmBucketArgs;
import org.apache.hadoop.ozone.om.helpers.OmBucketInfo;
import org.apache.hadoop.ozone.om.helpers.OmDBAccessIdInfo;
import org.apache.hadoop.ozone.om.helpers.OmDBKerberosPrincipalInfo;
+import org.apache.hadoop.ozone.om.helpers.OmDBTenantInfo;
import org.apache.hadoop.ozone.om.helpers.OmDeleteKeys;
import org.apache.hadoop.ozone.om.helpers.OmKeyArgs;
import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
@@ -137,6 +138,7 @@ import org.apache.hadoop.ozone.om.helpers.RepeatedOmKeyInfo;
import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
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.protocol.OMInterServiceProtocol;
import org.apache.hadoop.ozone.om.protocolPB.OMInterServiceProtocolClientSideImpl;
@@ -159,6 +161,7 @@ import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRoleI
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OzoneAclInfo;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ServicePort;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantAccessIdInfo;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantInfo;
import org.apache.hadoop.ozone.protocolPB.OMInterServiceProtocolServerSideImpl;
import org.apache.hadoop.ozone.storage.proto.OzoneManagerStorageProtos.PersistedUserVolumeInfo;
import org.apache.hadoop.ozone.protocolPB.OzoneManagerProtocolServerSideTranslatorPB;
@@ -253,6 +256,7 @@ import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.DETE
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.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;
import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_LOCK;
import static org.apache.hadoop.ozone.om.ratis.OzoneManagerRatisServer.RaftServerStatus.LEADER_AND_READY;
@@ -3077,15 +3081,77 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
"non-Ratis createTenant() is not implemented");
}
- // TODO: modify, delete
-
/**
- * Assign user to tenant.
+ * Assign user accessId to tenant.
*/
- public S3SecretValue assignUserToTenant(
+ public S3SecretValue tenantAssignUserAccessId(
String username, String tenantName, String accessId) throws IOException {
throw new NotImplementedException(
- "non-Ratis assignUserToTenant() is not implemented");
+ "non-Ratis tenantAssignUserAccessId() is not implemented");
+ }
+
+ /**
+ * Revoke user accessId to tenant.
+ */
+ public void tenantRevokeUserAccessId(String accessId) throws IOException {
+ throw new NotImplementedException(
+ "non-Ratis tenantRevokeUserAccessId() is not implemented");
+ }
+
+ /**
+ * Assign admin role to an accessId in a tenant.
+ */
+ public void tenantAssignAdmin(String accessId, String tenantName,
+ boolean delegated) throws IOException {
+ throw new NotImplementedException(
+ "non-Ratis tenantAssignAdmin() is not implemented");
+ }
+
+ /**
+ * Revoke admin role of an accessId from a tenant.
+ */
+ public void tenantRevokeAdmin(String accessId, String tenantName)
+ throws IOException {
+ throw new NotImplementedException(
+ "non-Ratis tenantRevokeAdmin() is not implemented");
+ }
+
+ /**
+ * List tenants.
+ */
+ public TenantInfoList listTenant() throws IOException {
+
+ final UserGroupInformation ugi = getRemoteUser();
+ if (!isAdmin(ugi)) {
+ final OMException omEx = new OMException(
+ "Only Ozone admins are allowed to list tenants.", PERMISSION_DENIED);
+ AUDIT.logWriteFailure(buildAuditMessageForFailure(
+ OMAction.LIST_TENANT, new LinkedHashMap<>(), omEx));
+ throw omEx;
+ }
+
+ final List<TenantInfo> tenantInfoList = new ArrayList<>();
+
+ TableIterator<String, ? extends Table.KeyValue<String, OmDBTenantInfo>>
+ iterator = metadataManager.getTenantStateTable().iterator();
+
+ while (iterator.hasNext()) {
+ final Table.KeyValue<String, OmDBTenantInfo> dbEntry = iterator.next();
+ final OmDBTenantInfo omDBTenantInfo = dbEntry.getValue();
+ assert(dbEntry.getKey().equals(omDBTenantInfo.getTenantName()));
+ tenantInfoList.add(TenantInfo.newBuilder()
+ .setTenantName(omDBTenantInfo.getTenantName())
+ .setBucketNamespaceName(omDBTenantInfo.getBucketNamespaceName())
+ .setAccountNamespaceName(omDBTenantInfo.getAccountNamespaceName())
+ .setUserPolicyGroupName(omDBTenantInfo.getUserPolicyGroupName())
+ .setBucketNamespaceName(omDBTenantInfo.getBucketPolicyGroupName())
+ .build());
+ }
+
+ AUDIT.logReadSuccess(buildAuditMessageForSuccess(
+ OMAction.LIST_TENANT, new LinkedHashMap<>()));
+
+ return new TenantInfoList(tenantInfoList);
}
/**
@@ -3109,26 +3175,34 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
final Set<String> accessIds = kerberosPrincipalInfo.getAccessIds();
final Map<String, String> auditMap = new LinkedHashMap<>();
- auditMap.put(OzoneConsts.TENANT, userPrincipal);
+ auditMap.put("userPrincipal", userPrincipal);
accessIds.forEach(accessId -> {
try {
- // Use get() intentionally, which throws if entry doesn't exist in table
final OmDBAccessIdInfo accessIdInfo =
metadataManager.getTenantAccessIdTable().get(accessId);
// Sanity check
+ if (accessIdInfo == null) {
+ LOG.error("Potential metadata error. Unexpected null accessIdInfo: "
+ + "entry for accessId '{}' doesn't exist in TenantAccessIdTable",
+ accessId);
+ throw new NullPointerException("accessIdInfo is null");
+ }
assert(accessIdInfo.getKerberosPrincipal().equals(userPrincipal));
- // Build TenantAccessIdInfo instances from accessId and tenantName
- final String tenantName = accessIdInfo.getTenantId();
accessIdInfoList.add(TenantAccessIdInfo.newBuilder()
.setAccessId(accessId)
- .setTenantName(tenantName)
+ .setTenantName(accessIdInfo.getTenantId())
+ .setIsAdmin(accessIdInfo.getIsAdmin())
+ .setIsDelegatedAdmin(accessIdInfo.getIsDelegatedAdmin())
.build());
} catch (IOException e) {
- LOG.error("Found potential DB consistency issue! "
- + "accessId '" + "' is supposed to exist in TenantAccessIdTable.");
+ LOG.error("Potential DB issue. Failed to retrieve OmDBAccessIdInfo "
+ + "for accessId '{}' in TenantAccessIdTable.", accessId);
+ // Audit
+ auditMap.put("accessId", accessId);
AUDIT.logWriteFailure(buildAuditMessageForFailure(
OMAction.TENANT_GET_USER_INFO, auditMap, e));
+ auditMap.remove("accessId");
}
});
@@ -4052,6 +4126,58 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
}
}
+ public boolean isTenantAdmin(UserGroupInformation callerUgi,
+ String tenantName, Boolean delegated) {
+ if (callerUgi == null) {
+ return false;
+ } else {
+ return isTenantAdmin(callerUgi.getShortUserName(), tenantName, delegated)
+ || isTenantAdmin(callerUgi.getUserName(), tenantName, delegated);
+ }
+ }
+
+ /**
+ * Returns true if user is a tenant's admin, false otherwise.
+ * @param username User name string.
+ * @param tenantName Tenant name string.
+ * @param delegated True if operation requires delegated admin permission.
+ */
+ public boolean isTenantAdmin(String username,
+ String tenantName, Boolean delegated) {
+ if (StringUtils.isEmpty(username) || StringUtils.isEmpty(tenantName)) {
+ return false;
+ }
+
+ try {
+ final OmDBKerberosPrincipalInfo principalInfo =
+ getMetadataManager().getPrincipalToAccessIdsTable().get(username);
+
+ if (principalInfo == null) {
+ // The user is not assigned to any tenant
+ return false;
+ }
+
+ // Find accessId assigned to the specified tenant
+ for (final String accessId : principalInfo.getAccessIds()) {
+ final OmDBAccessIdInfo accessIdInfo =
+ getMetadataManager().getTenantAccessIdTable().get(accessId);
+ if (tenantName.equals(accessIdInfo.getTenantId())) {
+ if (!delegated) {
+ return accessIdInfo.getIsAdmin();
+ } else {
+ return accessIdInfo.getIsAdmin()
+ && accessIdInfo.getIsDelegatedAdmin();
+ }
+ }
+ }
+ } catch (IOException e) {
+ LOG.error("Error while retrieving value for key '" + username
+ + "' in PrincipalToAccessIdsTable");
+ }
+
+ return false;
+ }
+
/**
* Returns true if OzoneNativeAuthorizer is enabled and false if otherwise.
*
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/codec/OMDBDefinition.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/codec/OMDBDefinition.java
index 11694c6..cb6cd6c 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/codec/OMDBDefinition.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/codec/OMDBDefinition.java
@@ -188,17 +188,7 @@ public class OMDBDefinition implements DBDefinition {
String.class,
new StringCodec());
- // Tables for S3 multi-tenancy
-
- // TODO: this table will be removed with the disappearance of CreateUser API.
- public static final DBColumnFamilyDefinition<String, String>
- TENANT_USER_TABLE =
- new DBColumnFamilyDefinition<>(
- OmMetadataManagerImpl.TENANT_USER_TABLE,
- String.class,
- new StringCodec(),
- String.class,
- new StringCodec());
+ // Tables for multi-tenancy
public static final DBColumnFamilyDefinition<String, OmDBAccessIdInfo>
TENANT_ACCESS_ID_TABLE =
@@ -274,7 +264,7 @@ public class OMDBDefinition implements DBDefinition {
BUCKET_TABLE, MULTIPART_INFO_TABLE, PREFIX_TABLE, DTOKEN_TABLE,
S3_SECRET_TABLE, TRANSACTION_INFO_TABLE, DIRECTORY_TABLE,
FILE_TABLE, OPEN_FILE_TABLE, DELETED_DIR_TABLE, META_TABLE,
- TENANT_USER_TABLE, TENANT_ACCESS_ID_TABLE,
+ TENANT_ACCESS_ID_TABLE,
PRINCIPAL_TO_ACCESS_IDS_TABLE, TENANT_STATE_TABLE, TENANT_GROUP_TABLE,
TENANT_ROLE_TABLE, TENANT_POLICY_TABLE };
}
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/utils/OzoneManagerRatisUtils.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/utils/OzoneManagerRatisUtils.java
index ec33456..eb28310 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/utils/OzoneManagerRatisUtils.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/utils/OzoneManagerRatisUtils.java
@@ -79,11 +79,13 @@ import org.apache.hadoop.ozone.om.request.s3.multipart.S3MultipartUploadComplete
import org.apache.hadoop.ozone.om.request.s3.multipart.S3MultipartUploadCompleteRequestWithFSO;
import org.apache.hadoop.ozone.om.request.s3.security.S3GetSecretRequest;
import org.apache.hadoop.ozone.om.request.s3.security.S3RevokeSecretRequest;
+import org.apache.hadoop.ozone.om.request.s3.tenant.OMAssignUserToTenantRequest;
+import org.apache.hadoop.ozone.om.request.s3.tenant.OMTenantAssignAdminRequest;
import org.apache.hadoop.ozone.om.request.s3.tenant.OMTenantCreateRequest;
import org.apache.hadoop.ozone.om.request.s3.tenant.OMTenantDeleteRequest;
import org.apache.hadoop.ozone.om.request.s3.tenant.OMTenantModifyRequest;
-import org.apache.hadoop.ozone.om.request.s3.tenant.OMAssignUserToTenantRequest;
-import org.apache.hadoop.ozone.om.request.s3.tenant.OMRevokeUserAccessToTenantRequest;
+import org.apache.hadoop.ozone.om.request.s3.tenant.OMTenantRevokeAdminRequest;
+import org.apache.hadoop.ozone.om.request.s3.tenant.OMTenantRevokeUserAccessIdRequest;
import org.apache.hadoop.ozone.om.request.security.OMCancelDelegationTokenRequest;
import org.apache.hadoop.ozone.om.request.security.OMGetDelegationTokenRequest;
import org.apache.hadoop.ozone.om.request.security.OMRenewDelegationTokenRequest;
@@ -263,10 +265,14 @@ public final class OzoneManagerRatisUtils {
return new OMTenantModifyRequest(omRequest);
case DeleteTenant:
return new OMTenantDeleteRequest(omRequest);
- case AssignUserToTenant:
+ case TenantAssignUserAccessId:
return new OMAssignUserToTenantRequest(omRequest);
- case RevokeUserAccessToTenant:
- return new OMRevokeUserAccessToTenantRequest(omRequest);
+ case TenantRevokeUserAccessId:
+ return new OMTenantRevokeUserAccessIdRequest(omRequest);
+ case TenantAssignAdmin:
+ return new OMTenantAssignAdminRequest(omRequest);
+ case TenantRevokeAdmin:
+ return new OMTenantRevokeAdminRequest(omRequest);
default:
throw new IllegalStateException("Unrecognized write command " +
"type request" + cmdType);
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 8359ace..9b4a747 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
@@ -35,12 +35,12 @@ import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerDoubleBufferHelper;
import org.apache.hadoop.ozone.om.request.OMClientRequest;
import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
import org.apache.hadoop.ozone.om.response.OMClientResponse;
-import org.apache.hadoop.ozone.om.response.s3.tenant.OMAssignUserToTenantResponse;
-import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.AssignUserToTenantRequest;
-import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.AssignUserToTenantResponse;
+import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantAssignUserAccessIdResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.S3Secret;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantAssignUserAccessIdRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantAssignUserAccessIdResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.UpdateGetS3SecretRequest;
import org.apache.http.auth.BasicUserPrincipal;
import org.slf4j.Logger;
@@ -52,13 +52,18 @@ import java.util.HashMap;
import java.util.Map;
import java.util.TreeSet;
+import static org.apache.hadoop.ozone.om.helpers.OmDBKerberosPrincipalInfo.SERIALIZATION_SPLIT_KEY;
import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.S3_SECRET_LOCK;
import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_LOCK;
+import static org.apache.hadoop.ozone.om.request.s3.tenant.OMTenantRequestHelper.checkTenantAdmin;
+import static org.apache.hadoop.ozone.om.request.s3.tenant.OMTenantRequestHelper.checkTenantExistence;
/*
- Ratis execution flow for OMTenantUserCreate
+ Ratis execution flow for OMAssignUserToTenant request:
+ (might be a bit outdated)
- Client (AssignUserToTenantHandler, etc.)
+ - Check admin privilege
- Check username validity: ensure no invalid characters
- Send request to server
- OMAssignUserToTenantRequest
@@ -94,6 +99,8 @@ import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_L
/**
* Handles OMAssignUserToTenantRequest.
+ *
+ * TODO: Rename this to OMTenantAssignUserAccessIdRequest after rebase.
*/
public class OMAssignUserToTenantRequest extends OMClientRequest {
public static final Logger LOG =
@@ -105,12 +112,16 @@ public class OMAssignUserToTenantRequest extends OMClientRequest {
@Override
public OMRequest preExecute(OzoneManager ozoneManager) throws IOException {
- final AssignUserToTenantRequest request =
- getOmRequest().getAssignUserToTenantRequest();
+ final TenantAssignUserAccessIdRequest request =
+ getOmRequest().getTenantAssignUserAccessIdRequest();
+
+ final String tenantName = request.getTenantName();
+
+ // Caller should be an Ozone admin or tenant delegated admin
+ checkTenantAdmin(ozoneManager, tenantName);
// Note: Tenant username _is_ the Kerberos principal of the user
final String tenantUsername = request.getTenantUsername();
- final String tenantName = request.getTenantName();
final String accessId = request.getAccessId();
// Check tenantUsername (user's Kerberos principal) validity. TODO: Check
@@ -127,8 +138,27 @@ public class OMAssignUserToTenantRequest extends OMClientRequest {
OMException.ResultCodes.INVALID_TENANT_NAME);
}
- // Won't check tenant existence in preExecute.
- // Won't check Kerberos principal existence.
+ // Check accessId validity.
+ if (accessId.contains(SERIALIZATION_SPLIT_KEY)) {
+ throw new OMException("Invalid accessId '" + accessId +
+ "'. accessId should not contain '" + SERIALIZATION_SPLIT_KEY + "'",
+ OMException.ResultCodes.INVALID_ACCESSID);
+ }
+
+ checkTenantExistence(ozoneManager.getMetadataManager(), tenantName);
+
+ // Below call implies user existence check in authorizer.
+ // If the user doesn't exist, Ranger return 400 and the call should throw.
+
+ // Call OMMTM
+ // Inform MultiTenantManager of user assignment so it could
+ // initialize some policies in Ranger.
+ final String roleId = ozoneManager.getMultiTenantManager()
+ .assignUserToTenant(new BasicUserPrincipal(tenantUsername), tenantName,
+ accessId);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("roleId that the user is assigned to: {}", roleId);
+ }
// Generate secret. Used only when doesn't the kerberosID entry doesn't
// exist in DB, discarded otherwise.
@@ -153,6 +183,20 @@ public class OMAssignUserToTenantRequest extends OMClientRequest {
}
@Override
+ public void handleRequestFailure(OzoneManager ozoneManager) {
+ final TenantAssignUserAccessIdRequest request =
+ getOmRequest().getTenantAssignUserAccessIdRequest();
+
+ try {
+ // Undo Authorizer states established in preExecute
+ ozoneManager.getMultiTenantManager().revokeUserAccessId(
+ request.getAccessId());
+ } catch (Exception e) {
+ // TODO: Ignore for now. See OMTenantCreateRequest#handleRequestFailure
+ }
+ }
+
+ @Override
@SuppressWarnings("checkstyle:methodlength")
public OMClientResponse validateAndUpdateCache(
OzoneManager ozoneManager, long transactionLogIndex,
@@ -172,21 +216,17 @@ public class OMAssignUserToTenantRequest extends OMClientRequest {
Map<String, String> auditMap = new HashMap<>();
OMMetadataManager omMetadataManager = ozoneManager.getMetadataManager();
- final AssignUserToTenantRequest request =
- getOmRequest().getAssignUserToTenantRequest();
+ final TenantAssignUserAccessIdRequest request =
+ getOmRequest().getTenantAssignUserAccessIdRequest();
final String tenantName = request.getTenantName();
- final String principal = request.getTenantUsername();
+ final String principal = request.getTenantUsername(); // TODO: Rename this
assert(accessId.equals(request.getAccessId()));
- final String volumeName = tenantName; // TODO: Configurable
IOException exception = null;
- String userId;
- try {
- // Check ACL: requires ozone admin or tenant admin permission
-// if (ozoneManager.getAclsEnabled()) {
-// // TODO: Call OMMultiTenantManager?
-// }
+ final String volumeName = OMTenantRequestHelper.getTenantVolumeName(
+ omMetadataManager, tenantName);
+ try {
acquiredVolumeLock = omMetadataManager.getLock().acquireWriteLock(
VOLUME_LOCK, volumeName);
@@ -201,13 +241,36 @@ public class OMAssignUserToTenantRequest extends OMClientRequest {
if (omMetadataManager.getTenantAccessIdTable().isExist(accessId)) {
LOG.error("accessId {} already exists", accessId);
throw new OMException("accessId '" + accessId + "' already exists!",
- OMException.ResultCodes.TENANT_USER_ALREADY_EXISTS);
+ OMException.ResultCodes.TENANT_USER_ACCESSID_ALREADY_EXISTS);
+ }
+
+ OmDBKerberosPrincipalInfo principalInfo = omMetadataManager
+ .getPrincipalToAccessIdsTable().getIfExist(principal);
+ // Reject if the user is already assigned to the tenant
+ if (principalInfo != null) {
+ // If any existing accessIds are assigned to the same tenant, throw ex
+ // TODO: There is room for perf improvement. add a map in OMMTM.
+ for (final String existingAccId : principalInfo.getAccessIds()) {
+ final OmDBAccessIdInfo accessIdInfo =
+ omMetadataManager.getTenantAccessIdTable().get(existingAccId);
+ if (accessIdInfo == null) {
+ LOG.error("Metadata error: accessIdInfo is null for accessId '{}'. "
+ + "Ignoring.", existingAccId);
+ throw new NullPointerException("accessIdInfo is null");
+ }
+ if (tenantName.equals(accessIdInfo.getTenantId())) {
+ throw new OMException("The same user is not allowed to be assigned "
+ + "to the same tenant more than once. User '" + principal
+ + "' is already assigned to tenant '" + tenantName + "' with "
+ + "accessId '" + existingAccId + "'.",
+ OMException.ResultCodes.TENANT_USER_ACCESSID_ALREADY_EXISTS);
+ }
+ }
}
- // Add to S3SecretTable.
- // TODO: dedupe - S3GetSecretRequest
+ // Add to S3SecretTable. TODO: Remove later.
acquiredS3SecretLock = omMetadataManager.getLock()
- .acquireWriteLock(S3_SECRET_LOCK, principal);
+ .acquireWriteLock(S3_SECRET_LOCK, accessId);
// Expect accessId absence from S3SecretTable
// TODO: This table might be merged with tenantAccessIdTable later.
@@ -225,47 +288,36 @@ public class OMAssignUserToTenantRequest extends OMClientRequest {
new CacheKey<>(accessId),
new CacheValue<>(Optional.of(s3SecretValue), transactionLogIndex));
- omMetadataManager.getLock().releaseWriteLock(S3_SECRET_LOCK, principal);
+ omMetadataManager.getLock().releaseWriteLock(S3_SECRET_LOCK, accessId);
acquiredS3SecretLock = false;
- // Inform MultiTenantManager of user assignment so it could
- // initialize some policies in Ranger.
- // TODO: Is userId from MultiTenantManager still useful?
- // TODO: Move this to preExecute as well.
- userId = ozoneManager.getMultiTenantManager()
- .assignUserToTenant(new BasicUserPrincipal(principal), tenantName,
- accessId);
- if (LOG.isDebugEnabled()) {
- LOG.debug("userId for assign user to tenant request = {}", userId);
- }
-
// Add to tenantAccessIdTable
final OmDBAccessIdInfo omDBAccessIdInfo = new OmDBAccessIdInfo.Builder()
- .setTenantName(tenantName)
+ .setTenantId(tenantName)
.setKerberosPrincipal(principal)
.setSharedSecret(s3SecretValue.getAwsSecret())
+ .setIsAdmin(false)
+ .setIsDelegatedAdmin(false)
.build();
omMetadataManager.getTenantAccessIdTable().addCacheEntry(
new CacheKey<>(accessId),
new CacheValue<>(Optional.of(omDBAccessIdInfo), transactionLogIndex));
// Add to principalToAccessIdsTable
- OmDBKerberosPrincipalInfo omDBKerberosPrincipalInfo = omMetadataManager
- .getPrincipalToAccessIdsTable().getIfExist(principal);
-
- if (omDBKerberosPrincipalInfo == null) {
- omDBKerberosPrincipalInfo = new OmDBKerberosPrincipalInfo.Builder()
+ if (principalInfo == null) {
+ principalInfo = new OmDBKerberosPrincipalInfo.Builder()
.setAccessIds(new TreeSet<>(Collections.singleton(accessId)))
.build();
} else {
- omDBKerberosPrincipalInfo.addAccessId(accessId);
+ principalInfo.addAccessId(accessId);
}
omMetadataManager.getPrincipalToAccessIdsTable().addCacheEntry(
new CacheKey<>(principal),
- new CacheValue<>(Optional.of(omDBKerberosPrincipalInfo),
+ new CacheValue<>(Optional.of(principalInfo),
transactionLogIndex));
// Add to tenantGroupTable
+ // TODO: DOUBLE CHECK GROUP NAME USAGE
final String defaultGroupName =
tenantName + OzoneConsts.DEFAULT_TENANT_USER_GROUP_SUFFIX;
omMetadataManager.getTenantGroupTable().addCacheEntry(
@@ -273,26 +325,30 @@ public class OMAssignUserToTenantRequest extends OMClientRequest {
new CacheValue<>(Optional.of(defaultGroupName), transactionLogIndex));
// Add to tenantRoleTable
- final String roleName = "role_admin";
+ // TODO: DOUBLE CHECK ROLENAME
+ final String roleName = "user";
omMetadataManager.getTenantRoleTable().addCacheEntry(
new CacheKey<>(accessId),
new CacheValue<>(Optional.of(roleName), transactionLogIndex));
- omResponse.setAssignUserToTenantResponse(
- AssignUserToTenantResponse.newBuilder().setSuccess(true)
+ // Generate response
+ omResponse.setTenantAssignUserAccessIdResponse(
+ TenantAssignUserAccessIdResponse.newBuilder().setSuccess(true)
.setS3Secret(S3Secret.newBuilder()
.setAwsSecret(awsSecret).setKerberosID(accessId))
.build());
- omClientResponse = new OMAssignUserToTenantResponse(omResponse.build(),
- s3SecretValue, principal, defaultGroupName, roleName,
- accessId, omDBAccessIdInfo, omDBKerberosPrincipalInfo);
+ omClientResponse = new OMTenantAssignUserAccessIdResponse(
+ omResponse.build(), s3SecretValue, principal, defaultGroupName,
+ roleName, accessId, omDBAccessIdInfo, principalInfo);
} catch (IOException ex) {
- ozoneManager.getMultiTenantManager().destroyUser(accessId);
+ // Error handling
+ handleRequestFailure(ozoneManager);
exception = ex;
// Set response success flag to false
- omResponse.setAssignUserToTenantResponse(
- AssignUserToTenantResponse.newBuilder().setSuccess(false).build());
- omClientResponse = new OMAssignUserToTenantResponse(
+ omResponse.setTenantAssignUserAccessIdResponse(
+ TenantAssignUserAccessIdResponse.newBuilder()
+ .setSuccess(false).build());
+ omClientResponse = new OMTenantAssignUserAccessIdResponse(
createErrorOMResponse(omResponse, ex));
} finally {
if (omClientResponse != null) {
@@ -300,7 +356,7 @@ public class OMAssignUserToTenantRequest extends OMClientRequest {
.add(omClientResponse, transactionLogIndex));
}
if (acquiredS3SecretLock) {
- omMetadataManager.getLock().releaseWriteLock(S3_SECRET_LOCK, principal);
+ omMetadataManager.getLock().releaseWriteLock(S3_SECRET_LOCK, accessId);
}
if (acquiredVolumeLock) {
omMetadataManager.getLock().releaseWriteLock(VOLUME_LOCK, volumeName);
@@ -309,19 +365,21 @@ public class OMAssignUserToTenantRequest extends OMClientRequest {
// Audit
auditMap.put(OzoneConsts.TENANT, tenantName);
- auditLog(ozoneManager.getAuditLogger(),
- buildAuditMessage(OMAction.ASSIGN_USER_TO_TENANT, auditMap, exception,
+ auditMap.put("user", principal);
+ auditMap.put("accessId", accessId);
+ auditLog(ozoneManager.getAuditLogger(), buildAuditMessage(
+ OMAction.TENANT_ASSIGN_USER_ACCESSID, auditMap, exception,
getOmRequest().getUserInfo()));
if (exception == null) {
LOG.info("Assigned user '{}' to tenant '{}' with accessId '{}'",
principal, tenantName, accessId);
- // TODO: omMetrics.incNumTenantUsers()
+ // TODO: omMetrics.incNumTenantAssignUser()
} else {
LOG.error("Failed to assign '{}' to tenant '{}' with accessId '{}': {}",
principal, tenantName, accessId, exception.getMessage());
// TODO: Check if the exception message is sufficient.
- // TODO: omMetrics.incNumTenantUserCreateFails()
+ // TODO: omMetrics.incNumTenantAssignUserFails()
}
return omClientResponse;
}
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMRevokeUserAccessToTenantRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMRevokeUserAccessToTenantRequest.java
deleted file mode 100644
index c542304..0000000
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMRevokeUserAccessToTenantRequest.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.request.s3.tenant;
-
-import org.apache.hadoop.ozone.om.OzoneManager;
-import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerDoubleBufferHelper;
-import org.apache.hadoop.ozone.om.request.volume.OMVolumeRequest;
-import org.apache.hadoop.ozone.om.response.OMClientResponse;
-import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
-
-import java.io.IOException;
-
-/**
- * Handles OMTenantUserModify request.
- */
-public class OMRevokeUserAccessToTenantRequest extends OMVolumeRequest {
-
- public OMRevokeUserAccessToTenantRequest(OMRequest omRequest) {
- super(omRequest);
- }
-
- @Override
- public OMRequest preExecute(OzoneManager ozoneManager) throws IOException {
- return getOmRequest();
- }
-
- @Override
- public OMClientResponse validateAndUpdateCache(
- OzoneManager ozoneManager, long transactionLogIndex,
- OzoneManagerDoubleBufferHelper ozoneManagerDoubleBufferHelper) {
-
- return null;
- }
-}
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantAssignAdminRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantAssignAdminRequest.java
new file mode 100644
index 0000000..dbd2fa5
--- /dev/null
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantAssignAdminRequest.java
@@ -0,0 +1,246 @@
+/*
+ * 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.request.s3.tenant;
+
+import com.google.common.base.Optional;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.hdds.utils.db.cache.CacheKey;
+import org.apache.hadoop.hdds.utils.db.cache.CacheValue;
+import org.apache.hadoop.ozone.OzoneConsts;
+import org.apache.hadoop.ozone.audit.OMAction;
+import org.apache.hadoop.ozone.om.OMMetadataManager;
+import org.apache.hadoop.ozone.om.OzoneManager;
+import org.apache.hadoop.ozone.om.exceptions.OMException;
+import org.apache.hadoop.ozone.om.helpers.OmDBAccessIdInfo;
+import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerDoubleBufferHelper;
+import org.apache.hadoop.ozone.om.request.OMClientRequest;
+import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
+import org.apache.hadoop.ozone.om.response.OMClientResponse;
+import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantAssignAdminResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantAssignAdminRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantAssignAdminResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_LOCK;
+
+/*
+ Execution flow
+
+ - preExecute
+ - Check caller admin privilege
+ - validateAndUpdateCache
+ - Update tenantAccessIdTable
+ */
+
+/**
+ * Handles OMTenantAssignAdminRequest.
+ */
+public class OMTenantAssignAdminRequest extends OMClientRequest {
+ public static final Logger LOG =
+ LoggerFactory.getLogger(OMTenantAssignAdminRequest.class);
+
+ public OMTenantAssignAdminRequest(OMRequest omRequest) {
+ super(omRequest);
+ }
+
+ @Override
+ public OMRequest preExecute(OzoneManager ozoneManager) throws IOException {
+ final TenantAssignAdminRequest request =
+ getOmRequest().getTenantAssignAdminRequest();
+
+ final String accessId = request.getAccessId();
+ String tenantName = request.getTenantName();
+
+ // If tenantName is not provided, figure it out from the table
+ if (StringUtils.isEmpty(tenantName)) {
+ tenantName = OMTenantRequestHelper.getTenantNameFromAccessId(
+ ozoneManager.getMetadataManager(), accessId);
+ assert(tenantName != null);
+ }
+
+ // Caller should be an Ozone admin or this tenant's delegated admin
+ OMTenantRequestHelper.checkTenantAdmin(ozoneManager, tenantName);
+
+ // TODO: Check tenant existence?
+
+ OmDBAccessIdInfo accessIdInfo = ozoneManager.getMetadataManager()
+ .getTenantAccessIdTable().get(accessId);
+
+ if (accessIdInfo == null) {
+ throw new OMException("accessId '" + accessId + "' not found.",
+ OMException.ResultCodes.TENANT_USER_ACCESSID_NOT_FOUND);
+ }
+
+ // Check if accessId is assigned to the tenant
+ if (!accessIdInfo.getTenantId().equals(tenantName)) {
+ throw new OMException("accessId '" + accessId +
+ "' must be assigned to tenant '" + tenantName + "' first.",
+ OMException.ResultCodes.INVALID_TENANT_NAME);
+ }
+
+ final boolean delegated;
+ if (request.hasDelegated()) {
+ delegated = request.getDelegated();
+ } else {
+ delegated = true;
+ }
+ // Call OMMTM to add user to tenant admin role
+ ozoneManager.getMultiTenantManager().assignTenantAdmin(
+ request.getAccessId(), delegated);
+
+ final OMRequest.Builder omRequestBuilder = getOmRequest().toBuilder()
+ .setUserInfo(getUserInfo())
+ .setTenantAssignAdminRequest(
+ TenantAssignAdminRequest.newBuilder()
+ .setAccessId(accessId)
+ .setTenantName(tenantName)
+ .setDelegated(delegated)
+ .build())
+ .setCmdType(getOmRequest().getCmdType())
+ .setClientId(getOmRequest().getClientId());
+
+ if (getOmRequest().hasTraceID()) {
+ omRequestBuilder.setTraceID(getOmRequest().getTraceID());
+ }
+
+ return omRequestBuilder.build();
+ }
+
+ @Override
+ public void handleRequestFailure(OzoneManager ozoneManager) {
+ final TenantAssignAdminRequest request =
+ getOmRequest().getTenantAssignAdminRequest();
+
+ try {
+ ozoneManager.getMultiTenantManager().revokeTenantAdmin(
+ request.getAccessId());
+ } catch (Exception e) {
+ // TODO: Ignore for now. See OMTenantCreateRequest#handleRequestFailure
+ }
+ }
+
+ @Override
+ @SuppressWarnings("checkstyle:methodlength")
+ public OMClientResponse validateAndUpdateCache(
+ OzoneManager ozoneManager, long transactionLogIndex,
+ OzoneManagerDoubleBufferHelper ozoneManagerDoubleBufferHelper) {
+
+ OMClientResponse omClientResponse = null;
+ final OMResponse.Builder omResponse =
+ OmResponseUtil.getOMResponseBuilder(getOmRequest());
+
+ final Map<String, String> auditMap = new HashMap<>();
+ OMMetadataManager omMetadataManager = ozoneManager.getMetadataManager();
+
+ final TenantAssignAdminRequest request =
+ getOmRequest().getTenantAssignAdminRequest();
+ final String accessId = request.getAccessId();
+ final String tenantName = request.getTenantName();
+ final boolean delegated = request.getDelegated();
+
+ boolean acquiredVolumeLock = false; // TODO: use tenant lock instead, maybe
+ IOException exception = null;
+
+ final String volumeName = OMTenantRequestHelper.getTenantVolumeName(
+ omMetadataManager, tenantName);
+
+ try {
+ acquiredVolumeLock = omMetadataManager.getLock().acquireWriteLock(
+ VOLUME_LOCK, volumeName);
+
+ final OmDBAccessIdInfo oldAccessIdInfo =
+ omMetadataManager.getTenantAccessIdTable().get(accessId);
+
+ if (oldAccessIdInfo == null) {
+ throw new OMException("OmDBAccessIdInfo entry is missing for accessId '"
+ + accessId + "'.", OMException.ResultCodes.METADATA_ERROR);
+ }
+
+ assert(oldAccessIdInfo.getTenantId().equals(tenantName));
+
+ // Update tenantAccessIdTable
+ final OmDBAccessIdInfo newOmDBAccessIdInfo =
+ new OmDBAccessIdInfo.Builder()
+ .setTenantId(oldAccessIdInfo.getTenantId())
+ .setKerberosPrincipal(oldAccessIdInfo.getKerberosPrincipal())
+ .setSharedSecret(oldAccessIdInfo.getSharedSecret())
+ .setIsAdmin(true)
+ .setIsDelegatedAdmin(delegated)
+ .build();
+ omMetadataManager.getTenantAccessIdTable().addCacheEntry(
+ new CacheKey<>(accessId),
+ new CacheValue<>(Optional.of(newOmDBAccessIdInfo),
+ transactionLogIndex));
+
+ // Update tenantRoleTable?
+// final String roleName = "role_admin";
+// omMetadataManager.getTenantRoleTable().addCacheEntry(
+// new CacheKey<>(accessId),
+// new CacheValue<>(Optional.of(roleName), transactionLogIndex));
+
+ omResponse.setTenantAssignAdminResponse(
+ TenantAssignAdminResponse.newBuilder().setSuccess(true).build());
+ omClientResponse = new OMTenantAssignAdminResponse(omResponse.build(),
+ accessId, newOmDBAccessIdInfo);
+
+ } catch (IOException ex) {
+ // Error handling
+ handleRequestFailure(ozoneManager);
+ exception = ex;
+ // Set success flag to false
+ omResponse.setTenantAssignAdminResponse(
+ TenantAssignAdminResponse.newBuilder().setSuccess(false).build());
+ omClientResponse = new OMTenantAssignAdminResponse(
+ createErrorOMResponse(omResponse, ex));
+ } finally {
+ if (omClientResponse != null) {
+ omClientResponse.setFlushFuture(ozoneManagerDoubleBufferHelper
+ .add(omClientResponse, transactionLogIndex));
+ }
+ if (acquiredVolumeLock) {
+ omMetadataManager.getLock().releaseWriteLock(VOLUME_LOCK, volumeName);
+ }
+ }
+
+ // Audit
+ auditMap.put(OzoneConsts.TENANT, tenantName);
+ auditLog(ozoneManager.getAuditLogger(), buildAuditMessage(
+ OMAction.TENANT_ASSIGN_ADMIN, auditMap, exception,
+ getOmRequest().getUserInfo()));
+
+ if (exception == null) {
+ LOG.info("Assigned admin to accessId '{}' in tenant '{}', "
+ + "delegated: {}", accessId, tenantName, delegated);
+ // TODO: omMetrics.incNumTenantAssignAdmin()
+ } else {
+ LOG.error("Failed to assign admin to accessId '{}' in tenant '{}', "
+ + "delegated: {}: {}",
+ accessId, tenantName, delegated, exception.getMessage());
+ // TODO: omMetrics.incNumTenantAssignAdminFails()
+ }
+ return omClientResponse;
+ }
+}
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 4cd62ee..49f329c 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
@@ -111,6 +111,9 @@ public class OMTenantCreateRequest extends OMVolumeRequest {
final CreateTenantRequest request = getOmRequest().getCreateTenantRequest();
final String tenantName = request.getTenantName();
+ // Check Ozone admin privilege
+ OMTenantRequestHelper.checkAdmin(ozoneManager);
+
// Check tenantName validity
if (tenantName.contains(OzoneConsts.TENANT_NAME_USER_NAME_DELIMITER)) {
throw new OMException("Invalid tenant name " + tenantName +
@@ -184,7 +187,7 @@ public class OMTenantCreateRequest extends OMVolumeRequest {
@Override
public void handleRequestFailure(OzoneManager ozoneManager) {
- CreateTenantRequest request = getOmRequest().getCreateTenantRequest();
+ final CreateTenantRequest request = getOmRequest().getCreateTenantRequest();
try {
final Tenant tenant = ozoneManager.getMultiTenantManager()
@@ -223,13 +226,10 @@ public class OMTenantCreateRequest extends OMVolumeRequest {
IOException exception = null;
final String tenantDefaultPolicies = request.getTenantDefaultPolicyName();
- assert(tenantDefaultPolicies != null);
-
- LOG.debug("Processing tenant '{}' create request", tenantName);
- LOG.debug("tenantDefaultPolicies = {}", tenantDefaultPolicies);
try {
- // Check ACL: requires volume create permission. TODO: tenant create perm?
+ // Check ACL: requires volume create permission.
+ // TODO: do we need a tenant create permission ? probably not
if (ozoneManager.getAclsEnabled()) {
checkAcls(ozoneManager, OzoneObj.ResourceType.VOLUME,
OzoneObj.StoreType.OZONE, IAccessAuthorizer.ACLType.CREATE,
@@ -251,7 +251,7 @@ public class OMTenantCreateRequest extends OMVolumeRequest {
// Add to tenantStateTable. Redundant assignment for clarity
final String bucketNamespaceName = tenantName;
- final String accountNamespaceName = tenantName;
+ final String accountNamespaceName = volumeName;
final String userPolicyGroupName =
tenantName + OzoneConsts.DEFAULT_TENANT_USER_POLICY_SUFFIX;
final String bucketPolicyGroupName =
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantRequestHelper.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantRequestHelper.java
new file mode 100644
index 0000000..9b03762
--- /dev/null
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantRequestHelper.java
@@ -0,0 +1,143 @@
+/*
+ * 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.request.s3.tenant;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.ipc.ProtobufRpcEngine;
+import org.apache.hadoop.ozone.om.OMMetadataManager;
+import org.apache.hadoop.ozone.om.OzoneManager;
+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.security.UserGroupInformation;
+
+import java.io.IOException;
+
+/**
+ * Utility class that contains helper methods for OM tenant requests.
+ */
+public final class OMTenantRequestHelper {
+
+ private OMTenantRequestHelper() {
+ }
+
+ /**
+ * Passes check only when caller is an Ozone admin,
+ * throws OMException otherwise.
+ * @throws OMException PERMISSION_DENIED
+ */
+ static void checkAdmin(OzoneManager ozoneManager) throws OMException {
+
+ final UserGroupInformation ugi = ProtobufRpcEngine.Server.getRemoteUser();
+ if (!ozoneManager.isAdmin(ugi)) {
+ throw new OMException("User '" + ugi.getUserName() +
+ "' is not an Ozone admin.",
+ OMException.ResultCodes.PERMISSION_DENIED);
+ }
+ }
+
+ /**
+ * Passes check if caller is an Ozone cluster admin or tenant delegated admin,
+ * throws OMException otherwise.
+ * @throws OMException PERMISSION_DENIED
+ */
+ static void checkTenantAdmin(OzoneManager ozoneManager, String tenantName)
+ throws OMException {
+
+ final UserGroupInformation ugi = ProtobufRpcEngine.Server.getRemoteUser();
+ if (!ozoneManager.isAdmin(ugi) &&
+ !ozoneManager.isTenantAdmin(ugi, tenantName, true)) {
+ throw new OMException("User '" + ugi.getUserName() +
+ "' is neither an Ozone admin nor a delegated admin of tenant '" +
+ tenantName + "'.", OMException.ResultCodes.PERMISSION_DENIED);
+ }
+ }
+
+ static void checkTenantExistence(OMMetadataManager omMetadataManager,
+ String tenantName) throws OMException {
+
+ try {
+ if (!omMetadataManager.getTenantStateTable().isExist(tenantName)) {
+ throw new OMException("Tenant '" + tenantName + "' doesn't exist.",
+ OMException.ResultCodes.TENANT_NOT_FOUND);
+ }
+ } catch (IOException ex) {
+ if (ex instanceof OMException) {
+ final OMException omEx = (OMException) ex;
+ if (omEx.getResult().equals(OMException.ResultCodes.TENANT_NOT_FOUND)) {
+ throw omEx;
+ }
+ }
+ throw new OMException("Unable to retrieve "
+ + "OmDBTenantInfo entry for tenant '" + tenantName + "': "
+ + ex.getMessage(), OMException.ResultCodes.METADATA_ERROR);
+ }
+ }
+
+ /**
+ * Retrieve volume name of the tenant.
+ */
+ static String getTenantVolumeName(OMMetadataManager omMetadataManager,
+ String tenantName) {
+
+ final OmDBTenantInfo tenantInfo;
+ try {
+ tenantInfo = omMetadataManager.getTenantStateTable().get(tenantName);
+ } catch (IOException e) {
+ throw new RuntimeException("Potential DB error. Unable to retrieve "
+ + "OmDBTenantInfo entry for tenant '" + tenantName + "'.");
+ }
+
+ if (tenantInfo == null) {
+ throw new RuntimeException("Potential DB error or race condition. "
+ + "OmDBTenantInfo entry is missing for tenant '" + tenantName + "'.");
+ }
+
+ final String volumeName = tenantInfo.getAccountNamespaceName();
+
+ if (StringUtils.isEmpty(tenantName)) {
+ throw new RuntimeException("Potential DB error. volumeName "
+ + "field is null or empty for tenantId '" + tenantName + "'.");
+ }
+
+ return volumeName;
+ }
+
+ static String getTenantNameFromAccessId(OMMetadataManager omMetadataManager,
+ String accessId) throws IOException {
+
+ final OmDBAccessIdInfo accessIdInfo = omMetadataManager
+ .getTenantAccessIdTable().get(accessId);
+
+ if (accessIdInfo == null) {
+ throw new OMException("OmDBAccessIdInfo entry is missing for accessId '" +
+ accessId + "'.", OMException.ResultCodes.METADATA_ERROR);
+ }
+
+ final String tenantName = accessIdInfo.getTenantId();
+
+ if (StringUtils.isEmpty(tenantName)) {
+ throw new OMException("tenantId field is null or empty for accessId '" +
+ accessId + "'.", OMException.ResultCodes.METADATA_ERROR);
+ }
+
+ return tenantName;
+ }
+
+}
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantRevokeAdminRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantRevokeAdminRequest.java
new file mode 100644
index 0000000..705dd89
--- /dev/null
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantRevokeAdminRequest.java
@@ -0,0 +1,221 @@
+/*
+ * 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.request.s3.tenant;
+
+import com.google.common.base.Optional;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.hdds.utils.db.cache.CacheKey;
+import org.apache.hadoop.hdds.utils.db.cache.CacheValue;
+import org.apache.hadoop.ozone.OzoneConsts;
+import org.apache.hadoop.ozone.audit.OMAction;
+import org.apache.hadoop.ozone.om.OMMetadataManager;
+import org.apache.hadoop.ozone.om.OzoneManager;
+import org.apache.hadoop.ozone.om.exceptions.OMException;
+import org.apache.hadoop.ozone.om.helpers.OmDBAccessIdInfo;
+import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerDoubleBufferHelper;
+import org.apache.hadoop.ozone.om.request.OMClientRequest;
+import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
+import org.apache.hadoop.ozone.om.response.OMClientResponse;
+import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantRevokeAdminResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantRevokeAdminRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantRevokeAdminResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_LOCK;
+
+/*
+ Execution flow
+
+ - preExecute
+ - Check caller admin privilege
+ - validateAndUpdateCache
+ - Update tenantAccessIdTable
+ */
+
+/**
+ * Handles OMTenantRevokeAdminRequest.
+ */
+public class OMTenantRevokeAdminRequest extends OMClientRequest {
+ public static final Logger LOG =
+ LoggerFactory.getLogger(OMTenantRevokeAdminRequest.class);
+
+ public OMTenantRevokeAdminRequest(OMRequest omRequest) {
+ super(omRequest);
+ }
+
+ @Override
+ public OMRequest preExecute(OzoneManager ozoneManager) throws IOException {
+ final TenantRevokeAdminRequest request =
+ getOmRequest().getTenantRevokeAdminRequest();
+
+ final String accessId = request.getAccessId();
+ String tenantName = request.getTenantName();
+
+ // If tenantName is not provided, figure it out from the table
+ if (StringUtils.isEmpty(tenantName)) {
+ tenantName = OMTenantRequestHelper.getTenantNameFromAccessId(
+ ozoneManager.getMetadataManager(), accessId);
+ }
+
+ // Caller should be an Ozone admin or this tenant's delegated admin
+ OMTenantRequestHelper.checkTenantAdmin(ozoneManager, tenantName);
+
+ // TODO: Check tenant existence?
+
+ OmDBAccessIdInfo accessIdInfo = ozoneManager.getMetadataManager()
+ .getTenantAccessIdTable().get(accessId);
+
+ if (accessIdInfo == null) {
+ throw new OMException("accessId '" + accessId + "' not found.",
+ OMException.ResultCodes.TENANT_USER_ACCESSID_NOT_FOUND);
+ }
+
+ // Check if accessId is assigned to the tenant
+ if (!accessIdInfo.getTenantId().equals(tenantName)) {
+ throw new OMException("accessId '" + accessId +
+ "' must be assigned to tenant '" + tenantName + "' first.",
+ OMException.ResultCodes.INVALID_TENANT_NAME);
+ }
+
+ // TODO: Call OMMTM to remove user from admin group of the tenant.
+ // The call should remove user (not accessId) from the tenant's admin group
+// ozoneManager.getMultiTenantManager().revokeTenantAdmin();
+
+ final OMRequest.Builder omRequestBuilder = getOmRequest().toBuilder()
+ .setUserInfo(getUserInfo())
+ .setTenantRevokeAdminRequest(request)
+ .setCmdType(getOmRequest().getCmdType())
+ .setClientId(getOmRequest().getClientId());
+
+ if (getOmRequest().hasTraceID()) {
+ omRequestBuilder.setTraceID(getOmRequest().getTraceID());
+ }
+
+ return omRequestBuilder.build();
+ }
+
+ @Override
+ @SuppressWarnings("checkstyle:methodlength")
+ public OMClientResponse validateAndUpdateCache(
+ OzoneManager ozoneManager, long transactionLogIndex,
+ OzoneManagerDoubleBufferHelper ozoneManagerDoubleBufferHelper) {
+
+ OMClientResponse omClientResponse = null;
+ final OMResponse.Builder omResponse =
+ OmResponseUtil.getOMResponseBuilder(getOmRequest());
+
+ final Map<String, String> auditMap = new HashMap<>();
+ OMMetadataManager omMetadataManager = ozoneManager.getMetadataManager();
+
+ final TenantRevokeAdminRequest request =
+ getOmRequest().getTenantRevokeAdminRequest();
+ final String accessId = request.getAccessId();
+ final String tenantName = request.getTenantName();
+
+ boolean acquiredVolumeLock = false; // TODO: use tenant lock instead, maybe
+ IOException exception = null;
+
+ final String volumeName = OMTenantRequestHelper.getTenantVolumeName(
+ omMetadataManager, tenantName);
+
+ try {
+ acquiredVolumeLock = omMetadataManager.getLock().acquireWriteLock(
+ VOLUME_LOCK, volumeName);
+
+ final OmDBAccessIdInfo oldAccessIdInfo =
+ omMetadataManager.getTenantAccessIdTable().get(accessId);
+
+ if (oldAccessIdInfo == null) {
+ throw new OMException("OmDBAccessIdInfo entry is missing for accessId '"
+ + accessId + "'.", OMException.ResultCodes.METADATA_ERROR);
+ }
+
+ assert(oldAccessIdInfo.getTenantId().equals(tenantName));
+
+ // Update tenantAccessIdTable
+ final OmDBAccessIdInfo newOmDBAccessIdInfo =
+ new OmDBAccessIdInfo.Builder()
+ .setTenantId(oldAccessIdInfo.getTenantId())
+ .setKerberosPrincipal(oldAccessIdInfo.getKerberosPrincipal())
+ .setSharedSecret(oldAccessIdInfo.getSharedSecret())
+ .setIsAdmin(false)
+ .setIsDelegatedAdmin(false)
+ .build();
+ omMetadataManager.getTenantAccessIdTable().addCacheEntry(
+ new CacheKey<>(accessId),
+ new CacheValue<>(Optional.of(newOmDBAccessIdInfo),
+ transactionLogIndex));
+
+ // Update tenantRoleTable?
+// final String roleName = "role_admin";
+// omMetadataManager.getTenantRoleTable().addCacheEntry(
+// new CacheKey<>(accessId),
+// new CacheValue<>(Optional.of(roleName), transactionLogIndex));
+
+ omResponse.setTenantRevokeAdminResponse(
+ TenantRevokeAdminResponse.newBuilder()
+ .setSuccess(true).build());
+ omClientResponse = new OMTenantRevokeAdminResponse(omResponse.build(),
+ accessId, newOmDBAccessIdInfo);
+
+ } catch (IOException ex) {
+ // Error handling: do nothing to Authorizer (Ranger) here?
+
+ exception = ex;
+ // Set success flag to false
+ omResponse.setTenantRevokeAdminResponse(
+ TenantRevokeAdminResponse.newBuilder()
+ .setSuccess(false).build());
+ omClientResponse = new OMTenantRevokeAdminResponse(
+ createErrorOMResponse(omResponse, ex));
+ } finally {
+ if (omClientResponse != null) {
+ omClientResponse.setFlushFuture(ozoneManagerDoubleBufferHelper
+ .add(omClientResponse, transactionLogIndex));
+ }
+ if (acquiredVolumeLock) {
+ omMetadataManager.getLock().releaseWriteLock(VOLUME_LOCK, volumeName);
+ }
+ }
+
+ // Audit
+ auditMap.put(OzoneConsts.TENANT, tenantName);
+ auditLog(ozoneManager.getAuditLogger(), buildAuditMessage(
+ OMAction.TENANT_REVOKE_ADMIN, auditMap, exception,
+ getOmRequest().getUserInfo()));
+
+ if (exception == null) {
+ LOG.info("Revoked admin of accessId '{}' from tenant '{}'",
+ accessId, tenantName);
+ // TODO: omMetrics.incNumTenantRevokeAdmin()
+ } else {
+ LOG.error("Failed to revoke admin of accessId '{}' from tenant '{}': {}",
+ accessId, tenantName, exception.getMessage());
+ // TODO: omMetrics.incNumTenantRevokeAdminFails()
+ }
+ return omClientResponse;
+ }
+}
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantRevokeUserAccessIdRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantRevokeUserAccessIdRequest.java
new file mode 100644
index 0000000..e364f12
--- /dev/null
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantRevokeUserAccessIdRequest.java
@@ -0,0 +1,251 @@
+/*
+ * 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.request.s3.tenant;
+
+import com.google.common.base.Optional;
+import org.apache.hadoop.hdds.utils.db.cache.CacheKey;
+import org.apache.hadoop.hdds.utils.db.cache.CacheValue;
+import org.apache.hadoop.ozone.OzoneConsts;
+import org.apache.hadoop.ozone.audit.OMAction;
+import org.apache.hadoop.ozone.om.OMMetadataManager;
+import org.apache.hadoop.ozone.om.OzoneManager;
+import org.apache.hadoop.ozone.om.exceptions.OMException;
+import org.apache.hadoop.ozone.om.helpers.OmDBAccessIdInfo;
+import org.apache.hadoop.ozone.om.helpers.OmDBKerberosPrincipalInfo;
+import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerDoubleBufferHelper;
+import org.apache.hadoop.ozone.om.request.OMClientRequest;
+import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
+import org.apache.hadoop.ozone.om.response.OMClientResponse;
+import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantRevokeUserAccessIdResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantRevokeUserAccessIdRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantRevokeUserAccessIdResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.S3_SECRET_LOCK;
+import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_LOCK;
+
+/*
+ Execution flow
+
+ - preExecute
+ - Check accessId existence
+ - Get tenantName from accessId
+ - Check caller Ozone admin or tenant admin privilege
+ - Throw if accessId is a tenant admin
+ - Call Authorizer
+ - validateAndUpdateCache
+ - Update DB tables
+ */
+
+/**
+ * Handles OMTenantRevokeUserAccessIdRequest request.
+ */
+public class OMTenantRevokeUserAccessIdRequest extends OMClientRequest {
+ public static final Logger LOG = LoggerFactory.getLogger(
+ OMTenantRevokeUserAccessIdRequest.class);
+
+ public OMTenantRevokeUserAccessIdRequest(OMRequest omRequest) {
+ super(omRequest);
+ }
+
+ @Override
+ public OMRequest preExecute(OzoneManager ozoneManager) throws IOException {
+ final TenantRevokeUserAccessIdRequest request =
+ getOmRequest().getTenantRevokeUserAccessIdRequest();
+
+ final String accessId = request.getAccessId();
+
+ // As of now, OMTenantRevokeUserAccessIdRequest does not get tenantName
+ // from the client, we just get it from the OM DB table. Uncomment
+ // below if we want the request to be similar to OMTenantRevokeAdminRequest
+// String tenantName = request.getTenantName();
+// if (tenantName == null) {
+// }
+
+ final OMMetadataManager omMetadataManager =
+ ozoneManager.getMetadataManager();
+ final OmDBAccessIdInfo accessIdInfo = omMetadataManager
+ .getTenantAccessIdTable().get(accessId);
+
+ if (accessIdInfo == null) {
+ // Note: This potentially leaks which accessIds exists in OM.
+ throw new OMException("accessId '" + accessId + "' doesn't exist",
+ OMException.ResultCodes.TENANT_USER_ACCESSID_NOT_FOUND);
+ }
+
+ final String tenantName = accessIdInfo.getTenantId();
+ assert(tenantName != null);
+ assert(tenantName.length() > 0);
+
+ // Caller should be an Ozone admin or this tenant's delegated admin
+ OMTenantRequestHelper.checkTenantAdmin(ozoneManager, tenantName);
+
+ if (accessIdInfo.getIsAdmin()) {
+ throw new OMException("accessId '" + accessId + "' is tenant admin of '" +
+ tenantName + "'. Revoke admin first.",
+ OMException.ResultCodes.PERMISSION_DENIED);
+ }
+
+ // Call OMMTM to revoke user access to tenant
+ // TODO: DOUBLE CHECK destroyUser() behavior
+ ozoneManager.getMultiTenantManager().revokeUserAccessId(accessId);
+
+ final OMRequest.Builder omRequestBuilder = getOmRequest().toBuilder()
+ .setUserInfo(getUserInfo())
+ .setTenantRevokeUserAccessIdRequest(
+ TenantRevokeUserAccessIdRequest.newBuilder()
+ .setAccessId(accessId)
+ .setTenantName(tenantName)
+ .build())
+ .setCmdType(getOmRequest().getCmdType())
+ .setClientId(getOmRequest().getClientId());
+
+ if (getOmRequest().hasTraceID()) {
+ omRequestBuilder.setTraceID(getOmRequest().getTraceID());
+ }
+
+ return omRequestBuilder.build();
+ }
+
+ @Override
+ public OMClientResponse validateAndUpdateCache(
+ OzoneManager ozoneManager, long transactionLogIndex,
+ OzoneManagerDoubleBufferHelper ozoneManagerDoubleBufferHelper) {
+
+ OMClientResponse omClientResponse = null;
+ final OzoneManagerProtocolProtos.OMResponse.Builder omResponse =
+ OmResponseUtil.getOMResponseBuilder(getOmRequest());
+
+ final Map<String, String> auditMap = new HashMap<>();
+ OMMetadataManager omMetadataManager = ozoneManager.getMetadataManager();
+
+ final TenantRevokeUserAccessIdRequest request =
+ getOmRequest().getTenantRevokeUserAccessIdRequest();
+ final String accessId = request.getAccessId();
+ final String tenantName = request.getTenantName();
+
+ boolean acquiredVolumeLock = false;
+ boolean acquiredS3SecretLock = false;
+ IOException exception = null;
+
+ final String volumeName = OMTenantRequestHelper.getTenantVolumeName(
+ omMetadataManager, tenantName);
+ String userPrincipal = null;
+
+ try {
+ acquiredVolumeLock =
+ omMetadataManager.getLock().acquireWriteLock(VOLUME_LOCK, volumeName);
+
+ // Remove from S3SecretTable. TODO: Remove later.
+ acquiredS3SecretLock = omMetadataManager.getLock()
+ .acquireWriteLock(S3_SECRET_LOCK, accessId);
+ omMetadataManager.getS3SecretTable().addCacheEntry(
+ new CacheKey<>(accessId),
+ new CacheValue<>(Optional.absent(), transactionLogIndex));
+ omMetadataManager.getLock().releaseWriteLock(S3_SECRET_LOCK,
+ accessId);
+ acquiredS3SecretLock = false;
+
+ // Remove accessId from principalToAccessIdsTable
+ OmDBAccessIdInfo omDBAccessIdInfo =
+ omMetadataManager.getTenantAccessIdTable().get(accessId);
+ assert(omDBAccessIdInfo != null);
+ userPrincipal = omDBAccessIdInfo.getKerberosPrincipal();
+ assert(userPrincipal != null);
+ OmDBKerberosPrincipalInfo principalInfo = omMetadataManager
+ .getPrincipalToAccessIdsTable().getIfExist(userPrincipal);
+ assert(principalInfo != null);
+ principalInfo.removeAccessId(accessId);
+ omMetadataManager.getPrincipalToAccessIdsTable().addCacheEntry(
+ new CacheKey<>(userPrincipal),
+ new CacheValue<>(principalInfo.getAccessIds().size() > 0 ?
+ // Invalidate (remove) the entry if accessIds set is empty
+ Optional.of(principalInfo) : Optional.absent(),
+ transactionLogIndex));
+
+ // Remove from TenantAccessIdTable
+ omMetadataManager.getTenantAccessIdTable().addCacheEntry(
+ new CacheKey<>(accessId),
+ new CacheValue<>(Optional.absent(), transactionLogIndex));
+
+ // Remove from tenantGroupTable
+ omMetadataManager.getTenantGroupTable().addCacheEntry(
+ new CacheKey<>(accessId),
+ new CacheValue<>(Optional.absent(), transactionLogIndex));
+
+ // Remove from tenantRoleTable
+ omMetadataManager.getTenantRoleTable().addCacheEntry(
+ new CacheKey<>(accessId),
+ new CacheValue<>(Optional.absent(), transactionLogIndex));
+
+ // Generate response
+ omResponse.setTenantRevokeUserAccessIdResponse(
+ TenantRevokeUserAccessIdResponse.newBuilder().setSuccess(true).build()
+ );
+ omClientResponse = new OMTenantRevokeUserAccessIdResponse(
+ omResponse.build(), accessId, userPrincipal, principalInfo);
+ } catch (IOException ex) {
+ // Error handling: do nothing to Authorizer here?
+ exception = ex;
+ // Set response success flag to false
+ omResponse.setTenantRevokeUserAccessIdResponse(
+ TenantRevokeUserAccessIdResponse.newBuilder()
+ .setSuccess(false).build());
+ omClientResponse = new OMTenantRevokeUserAccessIdResponse(
+ createErrorOMResponse(omResponse, ex));
+ } finally {
+ if (omClientResponse != null) {
+ omClientResponse.setFlushFuture(ozoneManagerDoubleBufferHelper
+ .add(omClientResponse, transactionLogIndex));
+ }
+ if (acquiredS3SecretLock) {
+ omMetadataManager.getLock().releaseWriteLock(S3_SECRET_LOCK, accessId);
+ }
+ if (acquiredVolumeLock) {
+ omMetadataManager.getLock().releaseWriteLock(VOLUME_LOCK, volumeName);
+ }
+ }
+
+ // Audit
+ auditMap.put(OzoneConsts.TENANT, tenantName);
+ auditMap.put("accessId", accessId);
+ auditMap.put("user", userPrincipal);
+ auditLog(ozoneManager.getAuditLogger(), buildAuditMessage(
+ OMAction.TENANT_REVOKE_USER_ACCESSID, auditMap, exception,
+ getOmRequest().getUserInfo()));
+
+ if (exception == null) {
+ LOG.info("Revoked user '{}' accessId '{}' to tenant '{}'",
+ userPrincipal, accessId, tenantName);
+ // TODO: omMetrics.incNumTenantRevokeUser()
+ } else {
+ LOG.error("Failed to revoke user '{}' accessId '{}' to tenant '{}': {}",
+ userPrincipal, accessId, tenantName, exception.getMessage());
+ // TODO: omMetrics.incNumTenantRevokeUserFails()
+ }
+ return omClientResponse;
+ }
+}
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMTenantAssignAdminResponse.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMTenantAssignAdminResponse.java
new file mode 100644
index 0000000..7b5c4a4
--- /dev/null
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMTenantAssignAdminResponse.java
@@ -0,0 +1,76 @@
+/*
+ * 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.response.s3.tenant;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.apache.hadoop.hdds.utils.db.BatchOperation;
+import org.apache.hadoop.ozone.om.OMMetadataManager;
+import org.apache.hadoop.ozone.om.helpers.OmDBAccessIdInfo;
+import org.apache.hadoop.ozone.om.response.CleanupTableInfo;
+import org.apache.hadoop.ozone.om.response.OMClientResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
+
+import javax.annotation.Nonnull;
+import java.io.IOException;
+
+import static org.apache.hadoop.ozone.om.OmMetadataManagerImpl.TENANT_ACCESS_ID_TABLE;
+
+/**
+ * Response for OMTenantAssignAdminRequest.
+ */
+@CleanupTableInfo(cleanupTables = {
+ TENANT_ACCESS_ID_TABLE
+// TENANT_ROLE_TABLE
+})
+public class OMTenantAssignAdminResponse extends OMClientResponse {
+
+ private String accessId;
+ private OmDBAccessIdInfo omDBAccessIdInfo;
+
+ public OMTenantAssignAdminResponse(@Nonnull OMResponse omResponse,
+ @Nonnull String accessId,
+ @Nonnull OmDBAccessIdInfo omDBAccessIdInfo
+ ) {
+ super(omResponse);
+ this.accessId = accessId;
+ this.omDBAccessIdInfo = omDBAccessIdInfo;
+ }
+
+ /**
+ * For when the request is not successful.
+ * For a successful request, the other constructor should be used.
+ */
+ public OMTenantAssignAdminResponse(@Nonnull OMResponse omResponse) {
+ super(omResponse);
+ checkStatusNotOK();
+ }
+
+ @Override
+ public void addToDBBatch(OMMetadataManager omMetadataManager,
+ BatchOperation batchOperation) throws IOException {
+
+ omMetadataManager.getTenantAccessIdTable().putWithBatch(
+ batchOperation, accessId, omDBAccessIdInfo);
+ }
+
+ @VisibleForTesting
+ public OmDBAccessIdInfo getOmDBAccessIdInfo() {
+ return omDBAccessIdInfo;
+ }
+}
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMAssignUserToTenantResponse.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMTenantAssignUserAccessIdResponse.java
similarity index 92%
copy from hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMAssignUserToTenantResponse.java
copy to hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMTenantAssignUserAccessIdResponse.java
index f8e1faf..eb9ec84 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMAssignUserToTenantResponse.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMTenantAssignUserAccessIdResponse.java
@@ -48,7 +48,7 @@ import static org.apache.hadoop.ozone.om.OmMetadataManagerImpl.TENANT_ROLE_TABLE
TENANT_GROUP_TABLE,
TENANT_ROLE_TABLE
})
-public class OMAssignUserToTenantResponse extends OMClientResponse {
+public class OMTenantAssignUserAccessIdResponse extends OMClientResponse {
private S3SecretValue s3SecretValue;
private String principal, groupName, roleName, accessId;
@@ -56,7 +56,7 @@ public class OMAssignUserToTenantResponse extends OMClientResponse {
private OmDBKerberosPrincipalInfo omDBKerberosPrincipalInfo;
@SuppressWarnings("checkstyle:parameternumber")
- public OMAssignUserToTenantResponse(@Nonnull OMResponse omResponse,
+ public OMTenantAssignUserAccessIdResponse(@Nonnull OMResponse omResponse,
@Nonnull S3SecretValue s3SecretValue,
@Nonnull String principal,
@Nonnull String groupName,
@@ -79,7 +79,7 @@ public class OMAssignUserToTenantResponse extends OMClientResponse {
* For when the request is not successful.
* For a successful request, the other constructor should be used.
*/
- public OMAssignUserToTenantResponse(@Nonnull OMResponse omResponse) {
+ public OMTenantAssignUserAccessIdResponse(@Nonnull OMResponse omResponse) {
super(omResponse);
checkStatusNotOK();
}
@@ -101,9 +101,9 @@ public class OMAssignUserToTenantResponse extends OMClientResponse {
omMetadataManager.getPrincipalToAccessIdsTable().putWithBatch(
batchOperation, principal, omDBKerberosPrincipalInfo);
omMetadataManager.getTenantGroupTable().putWithBatch(
- batchOperation, principal, groupName);
+ batchOperation, accessId, groupName);
omMetadataManager.getTenantRoleTable().putWithBatch(
- batchOperation, principal, roleName);
+ batchOperation, accessId, roleName);
}
@VisibleForTesting
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMTenantRevokeAdminResponse.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMTenantRevokeAdminResponse.java
new file mode 100644
index 0000000..d2faa36
--- /dev/null
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMTenantRevokeAdminResponse.java
@@ -0,0 +1,76 @@
+/*
+ * 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.response.s3.tenant;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.apache.hadoop.hdds.utils.db.BatchOperation;
+import org.apache.hadoop.ozone.om.OMMetadataManager;
+import org.apache.hadoop.ozone.om.helpers.OmDBAccessIdInfo;
+import org.apache.hadoop.ozone.om.response.CleanupTableInfo;
+import org.apache.hadoop.ozone.om.response.OMClientResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
+
+import javax.annotation.Nonnull;
+import java.io.IOException;
+
+import static org.apache.hadoop.ozone.om.OmMetadataManagerImpl.TENANT_ACCESS_ID_TABLE;
+
+/**
+ * Response for OMTenantAssignAdminRequest.
+ */
+@CleanupTableInfo(cleanupTables = {
+ TENANT_ACCESS_ID_TABLE
+// TENANT_ROLE_TABLE
+})
+public class OMTenantRevokeAdminResponse extends OMClientResponse {
+
+ private String accessId;
+ private OmDBAccessIdInfo omDBAccessIdInfo;
+
+ public OMTenantRevokeAdminResponse(@Nonnull OMResponse omResponse,
+ @Nonnull String accessId,
+ @Nonnull OmDBAccessIdInfo omDBAccessIdInfo
+ ) {
+ super(omResponse);
+ this.accessId = accessId;
+ this.omDBAccessIdInfo = omDBAccessIdInfo;
+ }
+
+ /**
+ * For when the request is not successful.
+ * For a successful request, the other constructor should be used.
+ */
+ public OMTenantRevokeAdminResponse(@Nonnull OMResponse omResponse) {
+ super(omResponse);
+ checkStatusNotOK();
+ }
+
+ @Override
+ public void addToDBBatch(OMMetadataManager omMetadataManager,
+ BatchOperation batchOperation) throws IOException {
+
+ omMetadataManager.getTenantAccessIdTable().putWithBatch(
+ batchOperation, accessId, omDBAccessIdInfo);
+ }
+
+ @VisibleForTesting
+ public OmDBAccessIdInfo getOmDBAccessIdInfo() {
+ return omDBAccessIdInfo;
+ }
+}
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMAssignUserToTenantResponse.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMTenantRevokeUserAccessIdResponse.java
similarity index 61%
rename from hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMAssignUserToTenantResponse.java
rename to hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMTenantRevokeUserAccessIdResponse.java
index f8e1faf..f3f8dac 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMAssignUserToTenantResponse.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMTenantRevokeUserAccessIdResponse.java
@@ -18,12 +18,9 @@
*/
package org.apache.hadoop.ozone.om.response.s3.tenant;
-import com.google.common.annotations.VisibleForTesting;
import org.apache.hadoop.hdds.utils.db.BatchOperation;
import org.apache.hadoop.ozone.om.OMMetadataManager;
-import org.apache.hadoop.ozone.om.helpers.OmDBAccessIdInfo;
import org.apache.hadoop.ozone.om.helpers.OmDBKerberosPrincipalInfo;
-import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
import org.apache.hadoop.ozone.om.response.CleanupTableInfo;
import org.apache.hadoop.ozone.om.response.OMClientResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
@@ -39,7 +36,7 @@ import static org.apache.hadoop.ozone.om.OmMetadataManagerImpl.TENANT_GROUP_TABL
import static org.apache.hadoop.ozone.om.OmMetadataManagerImpl.TENANT_ROLE_TABLE;
/**
- * Response for OMAssignUserToTenantRequest.
+ * Response for OMTenantRevokeUserAccessIdRequest.
*/
@CleanupTableInfo(cleanupTables = {
S3_SECRET_TABLE,
@@ -48,30 +45,20 @@ import static org.apache.hadoop.ozone.om.OmMetadataManagerImpl.TENANT_ROLE_TABLE
TENANT_GROUP_TABLE,
TENANT_ROLE_TABLE
})
-public class OMAssignUserToTenantResponse extends OMClientResponse {
+public class OMTenantRevokeUserAccessIdResponse extends OMClientResponse {
- private S3SecretValue s3SecretValue;
- private String principal, groupName, roleName, accessId;
- private OmDBAccessIdInfo omDBAccessIdInfo;
+ private String principal, accessId;
private OmDBKerberosPrincipalInfo omDBKerberosPrincipalInfo;
@SuppressWarnings("checkstyle:parameternumber")
- public OMAssignUserToTenantResponse(@Nonnull OMResponse omResponse,
- @Nonnull S3SecretValue s3SecretValue,
- @Nonnull String principal,
- @Nonnull String groupName,
- @Nonnull String roleName,
+ public OMTenantRevokeUserAccessIdResponse(@Nonnull OMResponse omResponse,
@Nonnull String accessId,
- @Nonnull OmDBAccessIdInfo omDBAccessIdInfo,
+ @Nonnull String principal,
@Nonnull OmDBKerberosPrincipalInfo omDBKerberosPrincipalInfo
) {
super(omResponse);
- this.s3SecretValue = s3SecretValue;
this.principal = principal;
- this.groupName = groupName;
- this.roleName = roleName;
this.accessId = accessId;
- this.omDBAccessIdInfo = omDBAccessIdInfo;
this.omDBKerberosPrincipalInfo = omDBKerberosPrincipalInfo;
}
@@ -79,7 +66,7 @@ public class OMAssignUserToTenantResponse extends OMClientResponse {
* For when the request is not successful.
* For a successful request, the other constructor should be used.
*/
- public OMAssignUserToTenantResponse(@Nonnull OMResponse omResponse) {
+ public OMTenantRevokeUserAccessIdResponse(@Nonnull OMResponse omResponse) {
super(omResponse);
checkStatusNotOK();
}
@@ -88,26 +75,27 @@ public class OMAssignUserToTenantResponse extends OMClientResponse {
public void addToDBBatch(OMMetadataManager omMetadataManager,
BatchOperation batchOperation) throws IOException {
- if (s3SecretValue != null &&
- getOMResponse().getStatus() == OzoneManagerProtocolProtos.Status.OK) {
- assert(accessId.equals(s3SecretValue.getKerberosID()));
- // Add S3SecretTable entry
- omMetadataManager.getS3SecretTable().putWithBatch(batchOperation,
- accessId, s3SecretValue);
+ assert(accessId != null);
+ // TODO: redundant check? Is status always OK when addToDBBatch is called
+ if (getOMResponse().getStatus() == OzoneManagerProtocolProtos.Status.OK) {
+ omMetadataManager.getS3SecretTable().deleteWithBatch(batchOperation,
+ accessId);
}
- omMetadataManager.getTenantAccessIdTable().putWithBatch(
- batchOperation, accessId, omDBAccessIdInfo);
- omMetadataManager.getPrincipalToAccessIdsTable().putWithBatch(
- batchOperation, principal, omDBKerberosPrincipalInfo);
- omMetadataManager.getTenantGroupTable().putWithBatch(
- batchOperation, principal, groupName);
- omMetadataManager.getTenantRoleTable().putWithBatch(
- batchOperation, principal, roleName);
- }
+ omMetadataManager.getTenantAccessIdTable().deleteWithBatch(
+ batchOperation, accessId);
+ omMetadataManager.getTenantGroupTable().deleteWithBatch(
+ batchOperation, accessId);
+ omMetadataManager.getTenantRoleTable().deleteWithBatch(
+ batchOperation, accessId);
- @VisibleForTesting
- public OmDBAccessIdInfo getOmDBAccessIdInfo() {
- return omDBAccessIdInfo;
+ if (omDBKerberosPrincipalInfo.getAccessIds().size() > 0) {
+ omMetadataManager.getPrincipalToAccessIdsTable().putWithBatch(
+ batchOperation, principal, omDBKerberosPrincipalInfo);
+ } else {
+ // Remove entry from DB if accessId set is empty
+ omMetadataManager.getPrincipalToAccessIdsTable().deleteWithBatch(
+ batchOperation, principal);
+ }
}
}
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 ad313d9..532b268 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
@@ -43,6 +43,7 @@ import org.apache.hadoop.ozone.om.helpers.OzoneFileStatus;
import org.apache.hadoop.ozone.om.helpers.RepeatedOmKeyInfo;
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.ratis.OzoneManagerDoubleBuffer;
import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerRatisUtils;
@@ -67,6 +68,8 @@ import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListBuc
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListBucketsResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListKeysRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListKeysResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListTenantRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListTenantResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListTrashRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListTrashResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListVolumeRequest;
@@ -236,6 +239,11 @@ public class OzoneManagerRequestHandler implements RequestHandler {
request.getTenantGetUserInfoRequest());
responseBuilder.setTenantGetUserInfoResponse(getUserInfoResponse);
break;
+ case ListTenant:
+ ListTenantResponse listTenantResponse = listTenant(
+ request.getListTenantRequest());
+ responseBuilder.setListTenantResponse(listTenantResponse);
+ break;
default:
responseBuilder.setSuccess(false);
responseBuilder.setMessage("Unrecognized Command Type: " + cmdType);
@@ -362,6 +370,7 @@ public class OzoneManagerRequestHandler implements RequestHandler {
private TenantGetUserInfoResponse tenantGetUserInfo(
TenantGetUserInfoRequest request) throws IOException {
+
final TenantGetUserInfoResponse.Builder resp =
TenantGetUserInfoResponse.newBuilder();
final String userPrincipal = request.getUserPrincipal();
@@ -377,6 +386,18 @@ public class OzoneManagerRequestHandler implements RequestHandler {
return resp.build();
}
+ private ListTenantResponse listTenant(
+ ListTenantRequest request) throws IOException {
+
+ final ListTenantResponse.Builder resp = ListTenantResponse.newBuilder();
+
+ TenantInfoList ret = impl.listTenant();
+ resp.setSuccess(true);
+ resp.addAllTenantInfo(ret.getTenantInfoList());
+
+ return resp.build();
+ }
+
private ListVolumeResponse listVolumes(ListVolumeRequest request)
throws IOException {
ListVolumeResponse.Builder resp = ListVolumeResponse.newBuilder();
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 0fe87d8..4a47251 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
@@ -39,14 +39,14 @@ import org.apache.hadoop.ozone.om.request.s3.tenant.OMAssignUserToTenantRequest;
import org.apache.hadoop.ozone.om.request.s3.tenant.OMTenantCreateRequest;
import org.apache.hadoop.ozone.om.response.OMClientResponse;
import org.apache.hadoop.ozone.om.response.s3.security.S3GetSecretResponse;
-import org.apache.hadoop.ozone.om.response.s3.tenant.OMAssignUserToTenantResponse;
+import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantAssignUserAccessIdResponse;
import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantCreateResponse;
-import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.AssignUserToTenantRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetS3SecretRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetS3SecretResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.S3Secret;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantAssignUserAccessIdRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.authentication.util.KerberosName;
@@ -163,9 +163,9 @@ public class TestS3GetSecretRequest {
return OMRequest.newBuilder()
.setClientId(UUID.randomUUID().toString())
- .setCmdType(Type.AssignUserToTenant)
- .setAssignUserToTenantRequest(
- AssignUserToTenantRequest.newBuilder()
+ .setCmdType(Type.TenantAssignUserAccessId)
+ .setTenantAssignUserAccessIdRequest(
+ TenantAssignUserAccessIdRequest.newBuilder()
.setTenantName(tenantNameStr)
.setTenantUsername(userPrincipalStr)
.setAccessId(accessIdStr)
@@ -367,17 +367,19 @@ public class TestS3GetSecretRequest {
txLogIndex, ozoneManagerDoubleBufferHelper);
// Check response type and cast
- Assert.assertTrue(omClientResponse instanceof OMAssignUserToTenantResponse);
- final OMAssignUserToTenantResponse omAssignUserToTenantResponse =
- (OMAssignUserToTenantResponse) omClientResponse;
+ Assert.assertTrue(
+ omClientResponse instanceof OMTenantAssignUserAccessIdResponse);
+ final OMTenantAssignUserAccessIdResponse
+ omTenantAssignUserAccessIdResponse =
+ (OMTenantAssignUserAccessIdResponse) omClientResponse;
// Check response
- Assert.assertTrue(omAssignUserToTenantResponse.getOMResponse()
+ Assert.assertTrue(omTenantAssignUserAccessIdResponse.getOMResponse()
.getSuccess());
- Assert.assertTrue(omAssignUserToTenantResponse.getOMResponse()
- .getAssignUserToTenantResponse().getSuccess());
+ Assert.assertTrue(omTenantAssignUserAccessIdResponse.getOMResponse()
+ .getTenantAssignUserAccessIdResponse().getSuccess());
final OmDBAccessIdInfo omDBAccessIdInfo =
- omAssignUserToTenantResponse.getOmDBAccessIdInfo();
+ omTenantAssignUserAccessIdResponse.getOmDBAccessIdInfo();
Assert.assertNotNull(omDBAccessIdInfo);
diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/GetUserInfoHandler.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/GetUserInfoHandler.java
index 44f0bda..c3ee7f8 100644
--- a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/GetUserInfoHandler.java
+++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/GetUserInfoHandler.java
@@ -69,11 +69,19 @@ public class GetUserInfoHandler extends TenantHandler {
out().println("User '" + userPrincipal + "' is assigned to:");
for (TenantAccessIdInfo accessIdInfo : accessIdInfoList) {
- out().println("- Tenant '" + accessIdInfo.getTenantName() +
- "' with accessId '" + accessIdInfo.getAccessId() + "'");
+ // Get admin info
+ final String adminInfoString;
+ if (accessIdInfo.getIsAdmin()) {
+ adminInfoString = accessIdInfo.getIsDelegatedAdmin() ?
+ " delegated admin" : " admin";
+ } else {
+ adminInfoString = "";
+ }
+ out().format("- Tenant '%s'%s with accessId '%s'%n",
+ accessIdInfo.getTenantName(), adminInfoString,
+ accessIdInfo.getAccessId());
}
- out().println();
} catch (IOException e) {
err().println("Failed to GetUserInfo of user '" + userPrincipal
+ "': " + e.getMessage());
diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantAssignAdminHandler.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantAssignAdminHandler.java
new file mode 100644
index 0000000..3e2c659
--- /dev/null
+++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantAssignAdminHandler.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.shell.tenant;
+
+import org.apache.hadoop.ozone.client.ObjectStore;
+import org.apache.hadoop.ozone.client.OzoneClient;
+import org.apache.hadoop.ozone.om.exceptions.OMException;
+import org.apache.hadoop.ozone.shell.OzoneAddress;
+import picocli.CommandLine;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.PERMISSION_DENIED;
+
+/**
+ * ozone tenant user assign-admin.
+ *
+ * User must already be assigned to tenant with, will be rejected otherwise.
+ */
+@CommandLine.Command(name = "assign-admin",
+ aliases = {"assignadmin"},
+ description = "Assign admin role to accessIds in a tenant")
+public class TenantAssignAdminHandler extends TenantHandler {
+
+ @CommandLine.Spec
+ private CommandLine.Model.CommandSpec spec;
+
+ @CommandLine.Parameters(description = "List of accessIds", arity = "1..")
+ private List<String> accessIds = new ArrayList<>();
+
+ @CommandLine.Option(names = {"-t", "--tenant"},
+ description = "Tenant name")
+ private String tenantName;
+
+ @CommandLine.Option(names = {"-d", "--delegated"}, defaultValue = "true",
+ description = "Make delegated admin")
+ private boolean delegated;
+
+ @Override
+ protected void execute(OzoneClient client, OzoneAddress address) {
+ final ObjectStore objStore = client.getObjectStore();
+
+ for (final String accessId : accessIds) {
+ try {
+ objStore.tenantAssignAdmin(accessId, tenantName, delegated);
+ // TODO: Make tenantAssignAdmin return accessId, tenantName, user later.
+ err().println("Assigned admin to '" + accessId +
+ (tenantName != null ? "' in tenant '" + tenantName + "'" : ""));
+ } catch (IOException e) {
+ err().println("Failed to assign admin to '" + accessId +
+ (tenantName != null ? "' in tenant '" + tenantName + "'" : "") +
+ ": " + e.getMessage());
+ if (e instanceof OMException) {
+ final OMException omEx = (OMException) e;
+ // Don't bother continuing the loop if current user isn't Ozone admin
+ if (omEx.getResult().equals(PERMISSION_DENIED)) {
+ break;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/AssignUserToTenantHandler.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantAssignUserAccessIdHandler.java
similarity index 79%
rename from hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/AssignUserToTenantHandler.java
rename to hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantAssignUserAccessIdHandler.java
index c5feee1..75bf672 100644
--- a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/AssignUserToTenantHandler.java
+++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantAssignUserAccessIdHandler.java
@@ -18,7 +18,6 @@
package org.apache.hadoop.ozone.shell.tenant;
import org.apache.commons.lang3.StringUtils;
-import org.apache.hadoop.hdds.cli.GenericCli;
import org.apache.hadoop.ozone.client.ObjectStore;
import org.apache.hadoop.ozone.client.OzoneClient;
import org.apache.hadoop.ozone.om.exceptions.OMException;
@@ -36,17 +35,18 @@ import static org.apache.hadoop.ozone.OzoneConsts.TENANT_NAME_USER_NAME_DELIMITE
* ozone tenant user assign.
*/
@CommandLine.Command(name = "assign",
- description = "Assign user to tenant")
-public class AssignUserToTenantHandler extends TenantHandler {
+ description = "Assign user accessId to tenant")
+public class TenantAssignUserAccessIdHandler extends TenantHandler {
@CommandLine.Spec
private CommandLine.Model.CommandSpec spec;
- @CommandLine.Parameters(description = "List of user Kerberos principal(s)")
- private List<String> principals = new ArrayList<>();
+ @CommandLine.Parameters(description = "List of user principals",
+ arity = "1..")
+ private List<String> userPrincipals = new ArrayList<>();
@CommandLine.Option(names = {"-t", "--tenant"},
- description = "Tenant name")
+ description = "Tenant name", required = true)
private String tenantName;
@CommandLine.Option(names = {"-a", "--access-id", "--accessId"},
@@ -59,10 +59,6 @@ public class AssignUserToTenantHandler extends TenantHandler {
// `s3 getsecret` and leak the secret if an admin isn't careful.
private String accessId;
- private boolean isEmptyList(List<String> list) {
- return list == null || list.size() == 0;
- }
-
private String getDefaultAccessId(String principal) {
return tenantName + TENANT_NAME_USER_NAME_DELIMITER + principal;
}
@@ -71,33 +67,23 @@ public class AssignUserToTenantHandler extends TenantHandler {
protected void execute(OzoneClient client, OzoneAddress address) {
final ObjectStore objStore = client.getObjectStore();
- if (isEmptyList(principals)) {
- GenericCli.missingSubcommand(spec);
- return;
- }
-
- if (StringUtils.isEmpty(tenantName)) {
- err().println("Please specify a tenant name with -t.");
- return;
- }
-
if (StringUtils.isEmpty(accessId)) {
- accessId = getDefaultAccessId(principals.get(0));
- } else if (principals.size() > 1) {
+ accessId = getDefaultAccessId(userPrincipals.get(0));
+ } else if (userPrincipals.size() > 1) {
err().println("Manually specifying accessId is only supported when there "
+ "is one user principal in the command line. Reduce the number of "
+ "principal to one and try again.");
return;
}
- for (int i = 0; i < principals.size(); i++) {
- final String principal = principals.get(i);
+ for (int i = 0; i < userPrincipals.size(); i++) {
+ final String principal = userPrincipals.get(i);
try {
if (i >= 1) {
accessId = getDefaultAccessId(principal);
}
final S3SecretValue resp =
- objStore.assignUserToTenant(principal, tenantName, accessId);
+ objStore.tenantAssignUserAccessId(principal, tenantName, accessId);
err().println("Assigned '" + principal + "' to '" + tenantName +
"' with accessId '" + accessId + "'.");
out().println("export AWS_ACCESS_KEY_ID='" +
diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantListHandler.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantListHandler.java
new file mode 100644
index 0000000..45f6ccf
--- /dev/null
+++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantListHandler.java
@@ -0,0 +1,90 @@
+/*
+ * 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 org.apache.hadoop.ozone.client.ObjectStore;
+import org.apache.hadoop.ozone.client.OzoneClient;
+import org.apache.hadoop.ozone.om.helpers.TenantInfoList;
+import org.apache.hadoop.ozone.shell.OzoneAddress;
+import picocli.CommandLine;
+
+import java.io.IOException;
+
+/**
+ * ozone tenant list.
+ */
+@CommandLine.Command(name = "list",
+ aliases = {"ls"},
+ description = "List tenants")
+public class TenantListHandler extends TenantHandler {
+
+// @CommandLine.Mixin
+// private ListOptions listOptions;
+
+// @CommandLine.Option(names = {"--json", "-j"},
+// description = "Print the result in JSON.")
+// private boolean printJson;
+
+ // TODO: long == json later.
+ @CommandLine.Option(names = {"--long"},
+ // Not using -l here as it potentially collides with -l inside ListOptions
+ // if we do need pagination at some point.
+ description = "List in long format")
+ private boolean longFormat;
+
+ @CommandLine.Option(names = {"--header", "-H"},
+ description = "Print header")
+ private boolean printHeader;
+
+ @Override
+ protected void execute(OzoneClient client, OzoneAddress address) {
+ final ObjectStore objStore = client.getObjectStore();
+ try {
+ TenantInfoList tenantInfoList = objStore.listTenant();
+
+ if (printHeader) {
+ // default console width 80 / 5 = 16. +1 for extra room. Change later?
+ out().format(longFormat ? "%-17s" : "%s%n",
+ "Tenant");
+ if (longFormat) {
+ // TODO: rename these fields?
+ // TODO: print JSON by default after rebase.
+ out().format("%-17s%-17s%-17s%s%n",
+ "BucketNS",
+ "AccountNS", // == Volume name IIRC ?
+ "UserPolicy",
+ "BucketPolicy");
+ }
+ }
+
+ tenantInfoList.getTenantInfoList().forEach(tenantInfo -> {
+ out().format(longFormat ? "%-17s" : "%s%n",
+ tenantInfo.getTenantName());
+ if (longFormat) {
+ out().format("%-17s%-17s%-17s%s%n",
+ tenantInfo.getBucketNamespaceName(),
+ tenantInfo.getAccountNamespaceName(),
+ tenantInfo.getUserPolicyGroupName(),
+ tenantInfo.getBucketPolicyGroupName());
+ }
+ });
+ } catch (IOException e) {
+ LOG.error("Failed to list tenants: {}", e.getMessage());
+ }
+ }
+}
diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantRevokeAdminHandler.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantRevokeAdminHandler.java
new file mode 100644
index 0000000..ad94a9a
--- /dev/null
+++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantRevokeAdminHandler.java
@@ -0,0 +1,74 @@
+/*
+ * 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 org.apache.hadoop.ozone.client.ObjectStore;
+import org.apache.hadoop.ozone.client.OzoneClient;
+import org.apache.hadoop.ozone.om.exceptions.OMException;
+import org.apache.hadoop.ozone.shell.OzoneAddress;
+import picocli.CommandLine;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.PERMISSION_DENIED;
+
+/**
+ * ozone tenant user revoke-admin.
+ */
+@CommandLine.Command(name = "revoke-admin",
+ aliases = {"revokeadmin"},
+ description = "Revoke admin role from accessIds in a tenant")
+public class TenantRevokeAdminHandler extends TenantHandler {
+
+ @CommandLine.Spec
+ private CommandLine.Model.CommandSpec spec;
+
+ @CommandLine.Parameters(description = "List of accessIds", arity = "1..")
+ private List<String> accessIds = new ArrayList<>();
+
+ @CommandLine.Option(names = {"-t", "--tenant"},
+ description = "Tenant name")
+ private String tenantName;
+
+ @Override
+ protected void execute(OzoneClient client, OzoneAddress address) {
+ final ObjectStore objStore = client.getObjectStore();
+
+ for (final String accessId : accessIds) {
+ try {
+ // TODO: Make tenantRevokeAdmin return accessId, tenantName, user later.
+ objStore.tenantRevokeAdmin(accessId, tenantName);
+ err().println("Revoked admin role of '" + accessId +
+ (tenantName != null ? "' from tenant '" + tenantName + "'" : ""));
+ } catch (IOException e) {
+ err().println("Failed to revoke admin role of '" + accessId +
+ (tenantName != null ? "' from tenant '" + tenantName + "'" : "") +
+ ": " + e.getMessage());
+ if (e instanceof OMException) {
+ final OMException omEx = (OMException) e;
+ // Don't bother continuing the loop if current user isn't Ozone admin
+ if (omEx.getResult().equals(PERMISSION_DENIED)) {
+ break;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/RevokeUserAccessToTenantHandler.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantRevokeUserAccessIdHandler.java
similarity index 58%
rename from hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/RevokeUserAccessToTenantHandler.java
rename to hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantRevokeUserAccessIdHandler.java
index 0c4cdf7..66f2f8f 100644
--- a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/RevokeUserAccessToTenantHandler.java
+++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantRevokeUserAccessIdHandler.java
@@ -17,19 +17,40 @@
*/
package org.apache.hadoop.ozone.shell.tenant;
+import org.apache.hadoop.ozone.client.ObjectStore;
import org.apache.hadoop.ozone.client.OzoneClient;
import org.apache.hadoop.ozone.shell.OzoneAddress;
import picocli.CommandLine;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* ozone tenant user revoke.
*/
@CommandLine.Command(name = "revoke",
- description = "Revoke user access to tenant")
-public class RevokeUserAccessToTenantHandler extends TenantHandler {
+ description = "Revoke user accessId to tenant")
+public class TenantRevokeUserAccessIdHandler extends TenantHandler {
+
+ @CommandLine.Spec
+ private CommandLine.Model.CommandSpec spec;
+
+ @CommandLine.Parameters(description = "List of user accessIds", arity = "1..")
+ private List<String> accessIds = new ArrayList<>();
@Override
protected void execute(OzoneClient client, OzoneAddress address) {
- out().println("Not Implemented.");
+ final ObjectStore objStore = client.getObjectStore();
+
+ accessIds.forEach(accessId -> {
+ try {
+ objStore.tenantRevokeUserAccessId(accessId);
+ err().format("Revoked accessId '%s'.%n", accessId);
+ } catch (IOException e) {
+ err().format("Failed to revoke accessId '%s': %s%n",
+ accessId, e.getMessage());
+ }
+ });
}
}
diff --git a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantShell.java b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantShell.java
index f26e874..3f2c9f7 100644
--- a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantShell.java
+++ b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantShell.java
@@ -32,6 +32,7 @@ import java.util.function.Supplier;
TenantCreateHandler.class,
TenantModifyHandler.class,
TenantDeleteHandler.class,
+ TenantListHandler.class,
TenantUserCommands.class
})
public class TenantShell extends Shell {
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 49c58b5..591d3b0 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
@@ -36,8 +36,10 @@ import java.util.concurrent.Callable;
description = "Tenant user management",
subcommands = {
GetUserInfoHandler.class,
- AssignUserToTenantHandler.class,
- RevokeUserAccessToTenantHandler.class
+ TenantAssignUserAccessIdHandler.class,
+ TenantRevokeUserAccessIdHandler.class,
+ TenantAssignAdminHandler.class,
+ TenantRevokeAdminHandler.class
},
mixinStandardHelpOptions = true,
versionProvider = HddsVersionProvider.class)
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@ozone.apache.org
For additional commands, e-mail: commits-help@ozone.apache.org