You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@ozone.apache.org by GitBox <gi...@apache.org> on 2021/03/18 16:43:59 UTC

[GitHub] [ozone] prashantpogde opened a new pull request #2059: HDDS-4945. Initial prototype for MultiTenant support for Ozone.

prashantpogde opened a new pull request #2059:
URL: https://github.com/apache/ozone/pull/2059


   ## What changes were proposed in this pull request?
   
   Initial Prototype or Multitenenat support for Ozone. This patch is not complete yet. I will push more changes.
   
   ## What is the link to the Apache JIRA
   
   https://issues.apache.org/jira/browse/HDDS-4945
   
   ## How was this patch tested?
   
   Wrote some integration tests.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org


[GitHub] [ozone] smengcl closed pull request #2059: HDDS-4945. Initial prototype for MultiTenant support for Ozone.

Posted by GitBox <gi...@apache.org>.
smengcl closed pull request #2059:
URL: https://github.com/apache/ozone/pull/2059


   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org


[GitHub] [ozone] elek commented on pull request #2059: HDDS-4945. Initial prototype for MultiTenant support for Ozone.

Posted by GitBox <gi...@apache.org>.
elek commented on pull request #2059:
URL: https://github.com/apache/ozone/pull/2059#issuecomment-802103225


   Thanks to create this patch @prashantpogde. I always love to talk about proposals based on example code and proof of concepts.
   
   One missing point for me is the design doc and public discussion about the design. As far as I see it's not uploaded to the jira.
   
   While I am very grateful to have this which can make easier to understand the proposed design, I think we need to keep it in Draft state until the end of the discussion.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org


[GitHub] [ozone] prashantpogde commented on a change in pull request #2059: HDDS-4945. Initial prototype for MultiTenant support for Ozone.

Posted by GitBox <gi...@apache.org>.
prashantpogde commented on a change in pull request #2059:
URL: https://github.com/apache/ozone/pull/2059#discussion_r617138070



##########
File path: hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/AccountNameSpace.java
##########
@@ -0,0 +1,75 @@
+/*
+ * 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.multitenant;
+
+import org.apache.hadoop.hdds.annotation.InterfaceAudience;
+import org.apache.hadoop.hdds.annotation.InterfaceStability;
+import org.apache.hadoop.hdds.client.OzoneQuota;
+import org.apache.hadoop.hdds.fs.SpaceUsageSource;
+
+@InterfaceAudience.LimitedPrivate({"HDFS", "Yarn", "Ranger", "Hive", "HBase"})
+@InterfaceStability.Evolving
+
+public interface AccountNameSpace {
+  /**
+   * A Tenant will typically have his own AccountNameSpace to isolate the
+   * the users of this Tenancy from that of the others.
+   * An AccountNameSpace can have different attributes and
+   * restrictions/additional-privileges that could apply to the users of this
+   * AccountNameSpace.
+   * Example of AccountNameSpace attribute can include
+   *  - Space usage information across all the users of this account NameSpace
+   *  - Quota restrictions across all the users of this namespace
+   *  - Collective Bandwidth(Network usage information) usage across users of
+   *  this NameSpace over a time window.
+   *  - Any QOS restrictions.
+   *
+   *  Note that users can be explicitly given access to another Tenant's
+   *  BucketNameSpace. It is the AccountNameSpace QOS restriction of
+   *  user's Tenancy that will come into play when it comes to QOS enforcement.
+   *  Similarly Quotas can be enforce both/either for AccountNameSpace and/or
+   *  BucketNameSpace.
+   *
+   * All the users of this Tenant can be identified with an accessID
+   * AccountNameSpaceID$Username.
+   * @return AccountNameSpaceID
+   */
+  String getAccountNameSpaceID();
+
+  /**
+   * Get Space Usage Information for this AccountNameSpace. This can be
+   * used for billing purpose. Such Aggregation can also be done lazily
+   * by a Recon job. Implementations can decide.
+   * @return
+   */
+  SpaceUsageSource getSpaceUsage();
+
+  /**
+   * Sets quota for this AccountNameSpace. Quota enforcement can also be done
+   * Lazily by a Recon job but that would be a soft quota enforcement. Choice
+   * of quota enforcement style is left to Implementation.
+   * @param quota
+   */
+  void setQuota(OzoneQuota quota);

Review comment:
       We are keeping the APIs exported by the interface for now. This will be implemented over time.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org


[GitHub] [ozone] adoroszlai commented on pull request #2059: HDDS-4945. Initial prototype for MultiTenant support for Ozone.

Posted by GitBox <gi...@apache.org>.
adoroszlai commented on pull request #2059:
URL: https://github.com/apache/ozone/pull/2059#issuecomment-823810557


   > > separate these poc-s to the forks instead of creating PRs to avoid any confusions.
   > 
   > We are doing all this work in a separate branch. I will merge this from local fork to a branch.
   
   @prashantpogde I think @elek's suggestion was exactly the opposite.  Please keep the POC (the feature branch `HDDS-4944-S3-Multitenant-Users`) in a fork until it is stable enough.  Otherwise it will unnecessarily use Apache org's Github Actions quota, both for PRs and for keeping the feature branch up-to-date from `master`.
   
   It is possible to work together in anyone's fork, both PRs and CI are available there.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org


[GitHub] [ozone] prashantpogde commented on pull request #2059: HDDS-4945. Initial prototype for MultiTenant support for Ozone.

Posted by GitBox <gi...@apache.org>.
prashantpogde commented on pull request #2059:
URL: https://github.com/apache/ozone/pull/2059#issuecomment-802176661


   > Thanks to create this patch @prashantpogde. I always love to talk about proposals based on example code and proof of concepts.
   > 
   > One missing point for me is the design doc and public discussion about the design. As far as I see it's not uploaded to the jira.
   > 
   > While I am very grateful to have this which can make easier to understand the proposed design, I think we need to keep it in Draft state until the end of the discussion.
   
   @elek  Yes, this is just a draft state. This PR is incomplete. After some prototyping and proof of concept, I will share all the details as you mentioned.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org


[GitHub] [ozone] smengcl commented on a change in pull request #2059: HDDS-4945. Initial prototype for MultiTenant support for Ozone.

Posted by GitBox <gi...@apache.org>.
smengcl commented on a change in pull request #2059:
URL: https://github.com/apache/ozone/pull/2059#discussion_r605932848



##########
File path: hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantCreateRequest.java
##########
@@ -0,0 +1,300 @@
+/*
+ * 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.OmUtils;
+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.OmDBTenantInfo;
+import org.apache.hadoop.ozone.om.helpers.OmVolumeArgs;
+import org.apache.hadoop.ozone.om.multitenant.Tenant;
+import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerDoubleBufferHelper;
+import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
+import org.apache.hadoop.ozone.om.request.volume.OMVolumeRequest;
+import org.apache.hadoop.ozone.om.response.OMClientResponse;
+import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantCreateResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateVolumeRequest;
+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.VolumeInfo;
+import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer;
+import org.apache.hadoop.ozone.security.acl.OzoneObj;
+import org.apache.hadoop.ozone.storage.proto.OzoneManagerStorageProtos.PersistedUserVolumeInfo;
+import org.apache.hadoop.util.Time;
+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.USER_LOCK;
+import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_LOCK;
+
+/*
+  Ratis execution flow for OMTenantCreate
+
+- preExecute (perform checks and init)
+  - Check tenant name validity (again)
+    - If name is invalid, throw exception to client; else continue
+- validateAndUpdateCache (update DB)
+  - Grab VOLUME_LOCK write lock
+  - Check volume existence
+    - If tenant already exists, throw exception to client; else continue
+  - Check tenant existence by checking tenantStateTable keys
+    - If tenant already exists, throw exception to client; else continue
+  - tenantStateTable: New entry
+    - Key: tenant name. e.g. finance
+    - Value: new OmDBTenantInfo for the tenant
+      - tenantName: finance
+      - bucketNamespaceName: finance
+      - accountNamespaceName: finance
+      - userPolicyGroupName: finance-users
+      - bucketPolicyGroupName: finance-buckets
+  - tenantPolicyTable: Generate default policies for the new tenant
+    - K: finance-Users, V: finance-users-default
+    - K: finance-Buckets, V: finance-buckets-default
+  - Grab USER_LOCK write lock
+  - Create volume finance (See OMVolumeCreateRequest)
+  - Release VOLUME_LOCK write lock
+  - Release USER_LOCK write lock
+  - Queue Ranger policy sync that pushes default policies:
+      OMMultiTenantManager#createTenant
+ */
+
+/**
+ * Handles OMTenantCreate request.
+ */
+public class OMTenantCreateRequest extends OMVolumeRequest {
+  private static final Logger LOG =
+      LoggerFactory.getLogger(OMTenantCreateRequest.class);
+
+  public OMTenantCreateRequest(OMRequest omRequest) {
+    super(omRequest);
+  }
+
+  @Override
+  public OMRequest preExecute(OzoneManager ozoneManager) throws IOException {
+    final CreateTenantRequest request = getOmRequest().getCreateTenantRequest();
+    final String tenantName = request.getTenantName();
+
+    // Check tenantName validity
+    if (tenantName.contains(OzoneConsts.TENANT_NAME_USER_NAME_DELIMITER)) {
+      throw new OMException("Invalid tenant name " + tenantName +
+          ". Tenant name should not contain delimiter.",
+          OMException.ResultCodes.INVALID_VOLUME_NAME);
+    }
+
+    // getUserName returns:
+    // - Kerberos principal when Kerberos security is enabled
+    // - User's login name when security is not enabled
+    // - AWS_ACCESS_KEY_ID if the original request comes from S3 Gateway.
+    //    Not Applicable to TenantCreateRequest.
+    final String owner = getOmRequest().getUserInfo().getUserName();
+    final String volumeName = tenantName;  // TODO: Configurable
+    final VolumeInfo volumeInfo = VolumeInfo.newBuilder()
+        .setVolume(volumeName)
+        .setAdminName(owner)
+        .setOwnerName(owner)
+        .build();
+    // Verify volume name
+    OmUtils.validateVolumeName(volumeInfo.getVolume());
+
+    // Generate volume modification time
+    long initialTime = Time.now();
+    final VolumeInfo updatedVolumeInfo = volumeInfo.toBuilder()
+            .setCreationTime(initialTime)
+            .setModificationTime(initialTime)
+            .build();
+
+    final OMRequest.Builder omRequestBuilder = getOmRequest().toBuilder()
+        .setCreateTenantRequest(
+            CreateTenantRequest.newBuilder().setTenantName(tenantName))
+        .setCreateVolumeRequest(
+            CreateVolumeRequest.newBuilder().setVolumeInfo(updatedVolumeInfo))
+        .setUserInfo(getUserInfo())
+        .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;
+    OMResponse.Builder omResponse = OmResponseUtil.getOMResponseBuilder(
+        getOmRequest());
+    OmVolumeArgs omVolumeArgs;
+    boolean acquiredVolumeLock = false, acquiredUserLock = false;
+    Tenant tenant = null;
+    final String owner = getOmRequest().getUserInfo().getUserName();
+    LOG.info("owner: {}", owner);  // TODO: owner should be access_key_id
+    Map<String, String> auditMap = new HashMap<>();
+    OMMetadataManager omMetadataManager = ozoneManager.getMetadataManager();
+    CreateTenantRequest request = getOmRequest().getCreateTenantRequest();
+    final String tenantName = request.getTenantName();
+    final VolumeInfo volumeInfo =
+        getOmRequest().getCreateVolumeRequest().getVolumeInfo();
+    final String volumeName = volumeInfo.getVolume();
+    final String dbVolumeKey = omMetadataManager.getVolumeKey(volumeName);
+    IOException exception = null;
+    try {
+      // Check ACL: requires volume create permission. TODO: tenant create perm?
+      if (ozoneManager.getAclsEnabled()) {
+        checkAcls(ozoneManager, OzoneObj.ResourceType.VOLUME,
+            OzoneObj.StoreType.OZONE, IAccessAuthorizer.ACLType.CREATE,
+            tenantName, null, null);
+      }
+
+      acquiredVolumeLock = omMetadataManager.getLock().acquireWriteLock(
+          VOLUME_LOCK, volumeName);
+      // Check volume existence
+      if (omMetadataManager.getVolumeTable().isExist(volumeName)) {
+        LOG.debug("volume: {} already exists", volumeName);
+        throw new OMException("Volume already exists",
+            OMException.ResultCodes.VOLUME_ALREADY_EXISTS);
+      }
+      // Check tenant existence in tenantStateTable
+      if (omMetadataManager.getTenantStateTable().isExist(tenantName)) {
+        LOG.debug("tenant: {} already exists", tenantName);
+        throw new OMException("Tenant already exists",
+            OMException.ResultCodes.TENANT_ALREADY_EXISTS);
+      }
+
+      // Add to tenantStateTable. Redundant assignment for clarity
+      final String bucketNamespaceName = tenantName;
+      final String accountNamespaceName = tenantName;
+      final String userPolicyGroupName =
+          tenantName + OzoneConsts.DEFAULT_TENANT_USER_POLICY_SUFFIX;
+      final String bucketPolicyGroupName =
+          tenantName + OzoneConsts.DEFAULT_TENANT_BUCKET_POLICY_SUFFIX;
+      final OmDBTenantInfo omDBTenantInfo = new OmDBTenantInfo(
+          tenantName, bucketNamespaceName, accountNamespaceName,
+          userPolicyGroupName, bucketPolicyGroupName);
+      omMetadataManager.getTenantStateTable().addCacheEntry(
+          new CacheKey<>(tenantName),
+          new CacheValue<>(Optional.of(omDBTenantInfo), transactionLogIndex));
+
+      // Call OMMultiTenantManager
+//      tenant = ozoneManager.getMultiTenantManager().createTenant(tenantName);

Review comment:
       @prashantpogde 




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org


[GitHub] [ozone] prashantpogde commented on a change in pull request #2059: HDDS-4945. Initial prototype for MultiTenant support for Ozone.

Posted by GitBox <gi...@apache.org>.
prashantpogde commented on a change in pull request #2059:
URL: https://github.com/apache/ozone/pull/2059#discussion_r617138747



##########
File path: hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManagerImpl.java
##########
@@ -0,0 +1,316 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.hadoop.ozone.om;
+
+import static org.apache.hadoop.ozone.om.multitenant.AccessPolicy.AccessGrantType.ALLOW;
+import static org.apache.hadoop.ozone.om.multitenant.OzoneMultiTenantPrincipal.OzonePrincipalType.GROUP_PRINCIPAL;
+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;
+import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.READ;
+import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.READ_ACL;
+import static org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.BUCKET;
+import static org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.KEY;
+import static org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.VOLUME;
+import static org.apache.hadoop.ozone.security.acl.OzoneObj.StoreType.OZONE;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.ozone.om.multitenant.AccessPolicy;
+import org.apache.hadoop.ozone.om.multitenant.AccountNameSpace;
+import org.apache.hadoop.ozone.om.multitenant.BucketNameSpace;
+import org.apache.hadoop.ozone.om.multitenant.CephCompatibleTenantImpl;
+import org.apache.hadoop.ozone.om.multitenant.MultiTenantGateKeeper;
+import org.apache.hadoop.ozone.om.multitenant.MultiTenantGateKeeperRangerPlugin;
+import org.apache.hadoop.ozone.om.multitenant.OzoneMultiTenantPrincipal;
+import org.apache.hadoop.ozone.om.multitenant.RangerAccessPolicy;
+import org.apache.hadoop.ozone.om.multitenant.Tenant;
+import org.apache.hadoop.ozone.om.multitenantImpl.OzoneMultiTenantPrincipalImpl;
+import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType;
+import org.apache.hadoop.ozone.security.acl.OzoneObj;
+import org.apache.hadoop.ozone.security.acl.OzoneObjInfo;
+import org.apache.http.auth.BasicUserPrincipal;
+
+public class OMMultiTenantManagerImpl implements OMMultiTenantManager {
+  private MultiTenantGateKeeper gateKeeper;
+  private OMMetadataManager omMetadataManager;
+  private OzoneConfiguration conf;
+  private Map<String, Tenant> allTenants;
+
+  OMMultiTenantManagerImpl(OMMetadataManager mgr, OzoneConfiguration conf)
+      throws IOException {
+    this.conf = conf;
+    allTenants = new ConcurrentHashMap<>();
+    omMetadataManager = mgr;
+    start(conf);
+  }
+
+  @Override
+  public void start(OzoneConfiguration configuration) throws IOException {
+    gateKeeper = new MultiTenantGateKeeperRangerPlugin();

Review comment:
       yup. This will change, in the final productized version.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org


[GitHub] [ozone] avijayanhwx commented on a change in pull request #2059: HDDS-4945. Initial prototype for MultiTenant support for Ozone.

Posted by GitBox <gi...@apache.org>.
avijayanhwx commented on a change in pull request #2059:
URL: https://github.com/apache/ozone/pull/2059#discussion_r605264802



##########
File path: hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/BucketNameSpace.java
##########
@@ -0,0 +1,91 @@
+/*
+ * 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.multitenant;
+
+import java.util.List;
+
+import org.apache.hadoop.hdds.annotation.InterfaceAudience;
+import org.apache.hadoop.hdds.annotation.InterfaceStability;
+import org.apache.hadoop.hdds.client.OzoneQuota;
+import org.apache.hadoop.hdds.fs.SpaceUsageSource;
+import org.apache.hadoop.ozone.security.acl.OzoneObj;
+
+@InterfaceAudience.LimitedPrivate({"HDFS", "Yarn", "Ranger", "Hive", "HBase"})
+@InterfaceStability.Evolving
+public interface BucketNameSpace {
+  /**
+   * A Tenant will typically have his own BucketNameSpace to isolate the
+   * the buckets of this Tenancy from that of others.
+   * A BucketNameSpace can have different attributes and
+   * restrictions that could apply to this BucketNameSpace.
+   * Example of BucketNameSpace attributes can include
+   * - Collective Space usage information across all the buckets in this
+   * BucketNameSpace.
+   * - Collective Quota restrictions across all the buckets of this
+   * BucketNamespace.
+   *
+   * BucketNameSpace can be determined from the user context. Alternatively
+   * APIs can use "BucketNameSpace:bucketName" naming convention.
+   * Public buckets require unique bucket-names across all bucket-NameSpaces.
+   *
+   * Later, we can provide an API to create/set a public bucket by linking
+   * the bucket in Tenant's bucketNameSpace to a globally unique bucket in
+   * S3V(default-bucketNameSpace).
+   *
+   * @return BucketNameSpace-ID.
+   */
+  String getBucketNameSpaceID();
+
+  /**
+   * Returns all the top level Ozone objects that belong to a BucketNameSpace.
+   * Some implementation can choose to represent it by Single Volume. Nothing
+   * prevents any future extension where a bucketNameSpace can be multiple
+   * volumes as well (Example Use case: one for each user).
+   *
+   * @return List of Ozone Volumes.
+   */
+  List<OzoneObj> getBucketNameSpaceObjects();
+
+  /**
+   * Add one or more volumes to this BucketNameSpace.
+   * @param bucketNamespaceObject
+   */
+  void addBucketNameSpaceObjects(OzoneObj bucketNamespaceObject);

Review comment:
       nit. addBucketNameSpaceObject (without the 's')

##########
File path: hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/AccountNameSpace.java
##########
@@ -0,0 +1,75 @@
+/*
+ * 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.multitenant;
+
+import org.apache.hadoop.hdds.annotation.InterfaceAudience;
+import org.apache.hadoop.hdds.annotation.InterfaceStability;
+import org.apache.hadoop.hdds.client.OzoneQuota;
+import org.apache.hadoop.hdds.fs.SpaceUsageSource;
+
+@InterfaceAudience.LimitedPrivate({"HDFS", "Yarn", "Ranger", "Hive", "HBase"})
+@InterfaceStability.Evolving
+
+public interface AccountNameSpace {
+  /**
+   * A Tenant will typically have his own AccountNameSpace to isolate the
+   * the users of this Tenancy from that of the others.
+   * An AccountNameSpace can have different attributes and
+   * restrictions/additional-privileges that could apply to the users of this
+   * AccountNameSpace.
+   * Example of AccountNameSpace attribute can include
+   *  - Space usage information across all the users of this account NameSpace
+   *  - Quota restrictions across all the users of this namespace
+   *  - Collective Bandwidth(Network usage information) usage across users of
+   *  this NameSpace over a time window.
+   *  - Any QOS restrictions.
+   *
+   *  Note that users can be explicitly given access to another Tenant's
+   *  BucketNameSpace. It is the AccountNameSpace QOS restriction of
+   *  user's Tenancy that will come into play when it comes to QOS enforcement.
+   *  Similarly Quotas can be enforce both/either for AccountNameSpace and/or
+   *  BucketNameSpace.
+   *
+   * All the users of this Tenant can be identified with an accessID
+   * AccountNameSpaceID$Username.
+   * @return AccountNameSpaceID
+   */
+  String getAccountNameSpaceID();
+
+  /**
+   * Get Space Usage Information for this AccountNameSpace. This can be
+   * used for billing purpose. Such Aggregation can also be done lazily
+   * by a Recon job. Implementations can decide.
+   * @return
+   */
+  SpaceUsageSource getSpaceUsage();
+
+  /**
+   * Sets quota for this AccountNameSpace. Quota enforcement can also be done
+   * Lazily by a Recon job but that would be a soft quota enforcement. Choice
+   * of quota enforcement style is left to Implementation.
+   * @param quota
+   */
+  void setQuota(OzoneQuota quota);

Review comment:
       Is there a plan to allow updation of quota that was assigned to this Account Namepsace? If not, IMO we can get rid of this setter.

##########
File path: hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantUserCreateHandler.java
##########
@@ -0,0 +1,70 @@
+/*
+ * 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.s3;
+
+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.helpers.S3SecretValue;
+import org.apache.hadoop.ozone.shell.OzoneAddress;
+import picocli.CommandLine;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * ozone s3 user create.
+ */
+@CommandLine.Command(name = "create",
+    description = "Create one or more tenant users")
+public class TenantUserCreateHandler extends S3Handler {
+
+  @CommandLine.Spec
+  private CommandLine.Model.CommandSpec spec;
+
+  @CommandLine.Parameters(description = "List of tenant user short names")
+  private List<String> usernames = new ArrayList<>();
+
+  @CommandLine.Option(names = "-t",
+      description = "Tenant name")
+  private String tenantName;
+
+  @Override
+  protected void execute(OzoneClient client, OzoneAddress address) {
+    final ObjectStore objStore = client.getObjectStore();
+    if (tenantName == null || tenantName.length() == 0) {
+      tenantName = objStore.getS3VolumeName();
+    }
+    if (usernames.size() > 0) {
+      for (String username : usernames) {
+        try {
+          S3SecretValue res = objStore.createTenantUser(username, tenantName);
+          out().println("Successfully created tenant " + username + ":");

Review comment:
       nit. Successfully created **user**

##########
File path: hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/RangerAccessPolicy.java
##########
@@ -0,0 +1,250 @@
+/*
+ * 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.multitenant;
+
+import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType;
+import org.apache.hadoop.ozone.security.acl.OzoneObj;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.apache.hadoop.ozone.om.multitenant.AccessPolicy.AccessPolicyType.RANGER_POLICY;
+import static org.apache.hadoop.ozone.om.multitenant.OzoneMultiTenantPrincipal.OzonePrincipalType.GROUP_PRINCIPAL;
+
+public class RangerAccessPolicy implements AccessPolicy {
+
+  // For now RangerAccessPolicy supports only one object per policy
+  private OzoneObj accessObject;
+  private Map<String, List<AccessPolicyElem>> policyMap;
+  private String policyID;
+  private String policyJsonString;
+  private String policyName;
+
+  public RangerAccessPolicy(String name) {
+    policyMap = new ConcurrentHashMap<>();
+    policyName = name;
+  }
+
+  public void setPolicyID(String id) {
+    policyID = id;
+  }
+
+  public String getPolicyID() {
+    return policyID;
+  }
+
+  public String getPolicyName() {
+    return policyName;
+  }
+
+  @Override
+  public String getPolicyJsonString() throws Exception {
+    updatePolicyJsonString();
+    return policyJsonString;
+  }
+
+  @Override
+  public AccessPolicyType getAccessPolicyType() {
+    return RANGER_POLICY;
+  }
+
+  @Override
+  public void addAccessPolicyElem(OzoneObj object,
+                                  OzoneMultiTenantPrincipal principal,
+                                  ACLType acl, AccessGrantType grant)
+      throws IOException {
+    if (accessObject == null) {
+      accessObject = object;
+    } else if (!object.toString().equals(accessObject.toString())) {
+      throw new IOException(
+          "RangerAccessPolicy supports only one object per" + " policy");
+    }
+    AccessPolicyElem elem = new AccessPolicyElem(object, principal, acl, grant);
+    if (!policyMap.containsKey(principal.toString())) {
+      List<AccessPolicyElem> elemList = new ArrayList<>();
+      elemList.add(elem);
+      policyMap.put(principal.toString(), elemList);
+      return;
+    }
+    List<AccessPolicyElem> elemList = policyMap.get(principal.toString());
+    for (AccessPolicyElem e : elemList) {
+      if (e.getAclType() == acl) {
+        throw new IOException(
+            "RangerAccessPolicy: Principal " + principal.toString()
+                + " already exists with access " + acl);
+      }
+    }
+    elemList.add(elem);
+  }
+
+  @Override
+  public List<AccessPolicyElem> getAccessPolicyElem() {
+    List<AccessPolicyElem> list = new ArrayList<>();
+    for (Map.Entry<String, List<AccessPolicyElem>> entry : policyMap
+        .entrySet()) {
+      list.addAll(entry.getValue());
+    }
+    return list;
+  }
+
+  @Override public void removeAccessPolicyElem(OzoneObj object,
+      OzoneMultiTenantPrincipal principal, ACLType acl, AccessGrantType grant)
+      throws IOException {
+    if (accessObject == null) {
+      throw new IOException("removeAccessPolicyElem: Invalid Arguments.");
+    } else if (!object.toString().equals(accessObject.toString())) {
+      throw new IOException(
+          "removeAccessPolicyElem:  Object not found." + object.toString());
+    }
+    if (!policyMap.containsKey(principal.toString())) {
+      throw new IOException(
+          "removeAccessPolicyElem:  Principal not found." + object.toString());
+    }
+    List<AccessPolicyElem> elemList = policyMap.get(principal.toString());
+    for (AccessPolicyElem e : elemList) {
+      if (e.getAclType() == acl) {
+        elemList.remove(e);
+      }
+    }
+    if (elemList.isEmpty()) {
+      policyMap.remove(principal.toString());
+    }
+    throw new IOException(
+        "removeAccessPolicyElem:  aclType not found." + object.toString());
+  }
+
+  private String createRangerResourceItems() throws IOException {
+    StringBuilder resourceItems = new StringBuilder();
+    resourceItems.append("\"resources\":{" +
+        "\"volume\":{" +
+        "\"values\":[\"");
+    resourceItems.append(accessObject.getVolumeName());
+    resourceItems.append("\"]," +
+        "\"isRecursive\":false," +
+        "\"isExcludes\":false" +
+        "}");
+    if ((accessObject.getResourceType() == OzoneObj.ResourceType.BUCKET) ||
+        (accessObject.getResourceType() == OzoneObj.ResourceType.KEY)) {
+      resourceItems.append(
+          ",\"bucket\":{" +
+          "\"values\":[\"");
+      resourceItems.append(accessObject.getBucketName());
+      resourceItems.append("\"]," +
+          "\"isRecursive\":false," +
+          "\"isExcludes\":false" +
+          "}");
+    }
+    if (accessObject.getResourceType() == OzoneObj.ResourceType.KEY) {
+      resourceItems.append(",\"key\":{" +
+          "\"values\":[\"");
+      resourceItems.append(accessObject.getKeyName());
+      resourceItems.append("\"]," +
+          "\"isRecursive\":true," +
+          "\"isExcludes\":false" +
+          "}");
+    }
+    resourceItems.append("},");
+    return resourceItems.toString();
+  }
+
+  private String createRangerPolicyItems() throws IOException {
+    StringBuilder policyItems = new StringBuilder();
+    policyItems.append("\"policyItems\":[");
+    int mapRemainingSize = policyMap.size();
+    for (Map.Entry<String, List<AccessPolicyElem>> mapElem : policyMap
+        .entrySet()) {
+      mapRemainingSize--;
+      List<AccessPolicyElem> list = mapElem.getValue();
+      if (list.isEmpty()) {
+        continue;
+      }
+      policyItems.append("{");
+      if (list.get(0).getPrincipal().getUserPrincipalType()
+          == GROUP_PRINCIPAL) {
+        policyItems.append("\"groups\":[\"" + mapElem.getKey() + "\"],");
+      } else {
+        policyItems.append("\"users\":[\"" + mapElem.getKey() + "\"],");
+      }
+      policyItems.append("\"accesses\":[");
+      Iterator<AccessPolicyElem> iter = list.iterator();
+      while (iter.hasNext()) {
+        AccessPolicyElem elem = iter.next();
+        policyItems.append("{");
+        policyItems.append("\"type\":\"");
+        policyItems.append(getRangerAclString(elem.getAclType()));
+        policyItems.append("\",");
+        if (elem.getAccessGrantType() == AccessGrantType.ALLOW) {
+          policyItems.append("\"isAllowed\":true");
+        } else {
+          policyItems.append("\"isDenied\":true");
+        }
+        policyItems.append("}");
+        if (iter.hasNext()) {
+          policyItems.append(",");
+        }
+      }
+      policyItems.append("]");
+      policyItems.append("}");
+      if (mapRemainingSize > 0) {
+        policyItems.append(",");
+      }
+    }
+    policyItems.append("],");
+    return policyItems.toString();
+  }
+
+  private String getRangerAclString(ACLType aclType) throws IOException {
+    switch (aclType) {
+    case ALL:
+      return "All";
+    case LIST:
+      return "List";
+    case READ:
+      return "Read";
+    case WRITE:
+      return "Write";
+    case CREATE:
+      return "Create";
+    case DELETE:
+      return "Delete";
+    case READ_ACL:
+      return "Read_ACL";
+    case WRITE_ACL:
+      return "Write_ACL";
+    case NONE:
+      return "";
+    default:
+      throw new IOException("Unknown ACLType");
+    }
+  }
+
+  private void updatePolicyJsonString() throws Exception {
+    policyJsonString =
+        "{\"policyType\":\"0\"," + "\"name\":\"" + policyName + "\","
+            + "\"isEnabled\":true," + "\"policyPriority\":0,"
+            + "\"policyLabels\":[]," + "\"description\":\"\","
+            + "\"isAuditEnabled\":true," + createRangerResourceItems()
+            + "\"isDenyAllElse\":false," + createRangerPolicyItems()
+            + "\"allowExceptions\":[]," + "\"denyPolicyItems\":[],"
+            + "\"denyExceptions\":[]," + "\"service\":\"cm_ozone\"" + "}";

Review comment:
       In the future, service name "cm_ozone" may be something that Ozone needs to figure out from Ranger.

##########
File path: hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantGateKeeperRangerPlugin.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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.hadoop.ozone.om.multitenant;
+
+import 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.apache.hadoop.ozone.om.multitenant.AccessPolicy.AccessGrantType.ALLOW;
+import static org.apache.hadoop.ozone.om.multitenant.OzoneMultiTenantPrincipal.OzonePrincipalType.GROUP_PRINCIPAL;
+import static org.apache.hadoop.ozone.om.multitenant.OzoneMultiTenantPrincipal.OzonePrincipalType.USER_PRINCIPAL;
+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;
+import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.READ;
+import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.READ_ACL;
+import static org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.BUCKET;
+import static org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.KEY;
+import static org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.VOLUME;
+import static org.apache.hadoop.ozone.security.acl.OzoneObj.StoreType.OZONE;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.ozone.om.multitenantImpl.OzoneMultiTenantPrincipalImpl;
+import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType;
+import org.apache.hadoop.ozone.security.acl.OzoneObjInfo;
+import org.apache.http.auth.BasicUserPrincipal;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.Timeout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Tests TestMultiTenantGateKeeperImplWithRanger.
+ * Marking it as Ignore because it needs Ranger access point.
+ */
+@Ignore
+public class TestMultiTenantGateKeeperRangerPlugin {
+  private static final Logger LOG = LoggerFactory
+      .getLogger(TestMultiTenantGateKeeperRangerPlugin.class);
+
+  /**
+   * Set a timeout for each test.
+   */
+  @Rule
+  public Timeout timeout = new Timeout(300000);
+
+  // The following values need to be set before this test can be enabled.
+  private static final String RANGER_ENDPOINT = "";
+  private static final String RANGER_ENDPOINT_USER = "";
+  private static final String RANGER_ENDPOINT_USER_PASSWD = "";
+
+  private List<String> usersIdsCreated = new ArrayList<String>();
+  private List<String> groupIdsCreated = new ArrayList<String>();
+  private List<String> policyIdsCreated = new ArrayList<String>();
+
+  private static OzoneConfiguration conf;
+
+  @BeforeClass
+  public static void init() throws Exception {
+    conf = new OzoneConfiguration();
+    simulateOzoneSiteXmlConfig();
+  }
+
+  @AfterClass
+  public static void shutdown() {
+  }
+
+  private static void simulateOzoneSiteXmlConfig() {
+    conf.setStrings(OZONE_RANGER_HTTPS_ADDRESS_KEY, RANGER_ENDPOINT);
+    conf.setStrings(OZONE_OM_RANGER_HTTPS_ADMIN_API_USER, RANGER_ENDPOINT_USER);
+    conf.setStrings(OZONE_OM_RANGER_HTTPS_ADMIN_API_PASSWD,
+        RANGER_ENDPOINT_USER_PASSWD);
+  }
+
+  @Test
+  public void testMultiTenantGateKeeperRangerPlugin() throws Exception {
+    simulateOzoneSiteXmlConfig();
+    MultiTenantGateKeeper omm = new MultiTenantGateKeeperRangerPlugin();
+    omm.init(conf);
+
+    try {
+      OzoneMultiTenantPrincipal group1Principal = getTestPrincipal("tenant1",
+          "groupTestAdmin", GROUP_PRINCIPAL);
+      OzoneMultiTenantPrincipal group2Principal = getTestPrincipal("tenant1",
+          "groupTestUsers", GROUP_PRINCIPAL);
+      groupIdsCreated.add(omm.createGroup(group1Principal));
+      groupIdsCreated.add(omm.createGroup(group2Principal));
+
+      OzoneMultiTenantPrincipal userPrincipal =
+          getTestPrincipal("tenant1", "user1Test", USER_PRINCIPAL);
+      usersIdsCreated.add(omm.createUser(userPrincipal, groupIdsCreated));
+
+      AccessPolicy tenant1VolumeAccessPolicy = createVolumeAccessPolicy(
+          "vol1", "tenant1", "Users");
+      policyIdsCreated.add(omm.createAccessPolicy(tenant1VolumeAccessPolicy));
+
+      AccessPolicy tenant1BucketCreatePolicy = allowCreateBucketPolicy(
+          "vol1", "tenant1", "Users");
+      policyIdsCreated.add(omm.createAccessPolicy(tenant1BucketCreatePolicy));
+
+      AccessPolicy tenant1BucketAccessPolicy = allowAccessBucketPolicy(
+          "vol1", "tenant1", "Users", "bucket1");
+      policyIdsCreated.add(omm.createAccessPolicy(tenant1BucketAccessPolicy));
+
+      AccessPolicy tenant1KeyAccessPolicy = allowAccessKeyPolicy(
+          "vol1", "tenant1", "Users", "bucket1");
+      policyIdsCreated.add(omm.createAccessPolicy(tenant1KeyAccessPolicy));
+
+    } catch (Exception e) {
+      Assert.fail(e.getMessage());
+    } finally {
+      for (String id : policyIdsCreated) {
+        omm.deletePolicy(id);
+      }
+      if (usersIdsCreated.size() == 1) {
+        omm.deleteUser(usersIdsCreated.get(0));
+      }
+      for (String id : groupIdsCreated) {
+        omm.deleteGroup(id);
+      }
+    }
+  }
+
+  OzoneMultiTenantPrincipal getTestPrincipal(String tenant, String id,
+      OzoneMultiTenantPrincipal.OzonePrincipalType type) {
+    OzoneMultiTenantPrincipal principal = new OzoneMultiTenantPrincipalImpl(
+        new BasicUserPrincipal(tenant),
+        new BasicUserPrincipal(id), type);
+    return principal;
+  }
+
+  AccessPolicy createVolumeAccessPolicy(String vol, String tenant,

Review comment:
       Nit. These may be moved to a class which catalogs the list of well known policies, and provides static reference to creating them.

##########
File path: hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantUserCreateRequest.java
##########
@@ -0,0 +1,285 @@
+/*
+ * 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.codec.digest.DigestUtils;
+import org.apache.hadoop.hdds.utils.db.cache.CacheKey;
+import org.apache.hadoop.hdds.utils.db.cache.CacheValue;
+import org.apache.hadoop.ozone.OmUtils;
+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.S3SecretValue;
+import org.apache.hadoop.ozone.om.multitenant.OzoneMultiTenantPrincipal;
+import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerDoubleBufferHelper;
+import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
+import org.apache.hadoop.ozone.om.request.volume.OMVolumeRequest;
+import org.apache.hadoop.ozone.om.response.OMClientResponse;
+import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantCreateResponse;
+import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantUserCreateResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantUserRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantUserResponse;
+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.UpdateGetS3SecretRequest;
+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;
+
+/*
+  Ratis execution flow for OMTenantUserCreate
+
+- Client (UserCreateHandler , etc.)
+  - Check username validity: ensure no invalid characters
+  - Send request to server
+- OMTenantUserCreateRequest
+  - preExecute (perform checks and init)
+    - Check username validity (again), check $
+      - If username is invalid, throw exception to client; else continue
+    - Generate S3 secret for the new user
+  - validateAndUpdateCache (update DB)
+    - Permission check (checkACL need to check access key now)
+    - Grab VOLUME_LOCK write lock
+    - Check user existence
+      - If user doesn't exist, throw exception to client; else continue
+    - Check tenant existence
+      - If tenant doesn't exist, throw exception to client; else continue
+    - Grab S3_SECRET_LOCK write lock
+    - S3SecretTable: Flush generated S3 secret
+      - Key: TENANTNAME$USERNAME (equivalent to kerberosID)
+      - Value: <GENERATED_SECRET>
+    - Release S3_SECRET_LOCK write lock
+    - tenantUserTable: New entry
+      - Key: Tenant user name. e.g. finance$bob, s3v$alice
+      - Value: Tenant name. e.g. finance
+    - tenantGroupTable: Add this new user to the default tenant group.
+      - Key: finance$bob
+      - Value: finance-users
+    - tenantRoleTable: TBD. NoOp for prototype.
+    - Release VOLUME_LOCK write lock
+ */
+
+/**
+ * Handles OMTenantUserCreate request.
+ */
+public class OMTenantUserCreateRequest extends OMVolumeRequest {

Review comment:
       Is there a plan to provide a way get the s3 creds of a user from command line post creation? Something like an extension to the **ozone s3 getsecret** command.

##########
File path: hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantUserCreateHandler.java
##########
@@ -0,0 +1,70 @@
+/*
+ * 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.s3;
+
+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.helpers.S3SecretValue;
+import org.apache.hadoop.ozone.shell.OzoneAddress;
+import picocli.CommandLine;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * ozone s3 user create.
+ */
+@CommandLine.Command(name = "create",
+    description = "Create one or more tenant users")
+public class TenantUserCreateHandler extends S3Handler {
+
+  @CommandLine.Spec
+  private CommandLine.Model.CommandSpec spec;
+
+  @CommandLine.Parameters(description = "List of tenant user short names")
+  private List<String> usernames = new ArrayList<>();
+
+  @CommandLine.Option(names = "-t",
+      description = "Tenant name")
+  private String tenantName;
+
+  @Override
+  protected void execute(OzoneClient client, OzoneAddress address) {
+    final ObjectStore objStore = client.getObjectStore();
+    if (tenantName == null || tenantName.length() == 0) {
+      tenantName = objStore.getS3VolumeName();
+    }
+    if (usernames.size() > 0) {
+      for (String username : usernames) {
+        try {
+          S3SecretValue res = objStore.createTenantUser(username, tenantName);
+          out().println("Successfully created tenant " + username + ":");
+          out().println("export AWS_ACCESS_KEY_ID=" + res.getAwsAccessKey());
+          out().println("export AWS_SECRET_ACCESS_KEY=" + res.getAwsSecret());
+        } catch (IOException e) {
+          out().println("Failed to create tenant " + username + ": " +

Review comment:
       nit. same as above.

##########
File path: hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManagerImpl.java
##########
@@ -0,0 +1,316 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.hadoop.ozone.om;
+
+import static org.apache.hadoop.ozone.om.multitenant.AccessPolicy.AccessGrantType.ALLOW;
+import static org.apache.hadoop.ozone.om.multitenant.OzoneMultiTenantPrincipal.OzonePrincipalType.GROUP_PRINCIPAL;
+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;
+import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.READ;
+import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.READ_ACL;
+import static org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.BUCKET;
+import static org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.KEY;
+import static org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.VOLUME;
+import static org.apache.hadoop.ozone.security.acl.OzoneObj.StoreType.OZONE;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.ozone.om.multitenant.AccessPolicy;
+import org.apache.hadoop.ozone.om.multitenant.AccountNameSpace;
+import org.apache.hadoop.ozone.om.multitenant.BucketNameSpace;
+import org.apache.hadoop.ozone.om.multitenant.CephCompatibleTenantImpl;
+import org.apache.hadoop.ozone.om.multitenant.MultiTenantGateKeeper;
+import org.apache.hadoop.ozone.om.multitenant.MultiTenantGateKeeperRangerPlugin;
+import org.apache.hadoop.ozone.om.multitenant.OzoneMultiTenantPrincipal;
+import org.apache.hadoop.ozone.om.multitenant.RangerAccessPolicy;
+import org.apache.hadoop.ozone.om.multitenant.Tenant;
+import org.apache.hadoop.ozone.om.multitenantImpl.OzoneMultiTenantPrincipalImpl;
+import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType;
+import org.apache.hadoop.ozone.security.acl.OzoneObj;
+import org.apache.hadoop.ozone.security.acl.OzoneObjInfo;
+import org.apache.http.auth.BasicUserPrincipal;
+
+public class OMMultiTenantManagerImpl implements OMMultiTenantManager {
+  private MultiTenantGateKeeper gateKeeper;
+  private OMMetadataManager omMetadataManager;
+  private OzoneConfiguration conf;
+  private Map<String, Tenant> allTenants;
+
+  OMMultiTenantManagerImpl(OMMetadataManager mgr, OzoneConfiguration conf)
+      throws IOException {
+    this.conf = conf;
+    allTenants = new ConcurrentHashMap<>();
+    omMetadataManager = mgr;
+    start(conf);
+  }
+
+  @Override
+  public void start(OzoneConfiguration configuration) throws IOException {
+    gateKeeper = new MultiTenantGateKeeperRangerPlugin();

Review comment:
       nit. Type of plugin instance can be config driven in the future.

##########
File path: hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantCreateRequest.java
##########
@@ -0,0 +1,300 @@
+/*
+ * 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.OmUtils;
+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.OmDBTenantInfo;
+import org.apache.hadoop.ozone.om.helpers.OmVolumeArgs;
+import org.apache.hadoop.ozone.om.multitenant.Tenant;
+import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerDoubleBufferHelper;
+import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
+import org.apache.hadoop.ozone.om.request.volume.OMVolumeRequest;
+import org.apache.hadoop.ozone.om.response.OMClientResponse;
+import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantCreateResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateVolumeRequest;
+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.VolumeInfo;
+import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer;
+import org.apache.hadoop.ozone.security.acl.OzoneObj;
+import org.apache.hadoop.ozone.storage.proto.OzoneManagerStorageProtos.PersistedUserVolumeInfo;
+import org.apache.hadoop.util.Time;
+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.USER_LOCK;
+import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_LOCK;
+
+/*
+  Ratis execution flow for OMTenantCreate
+
+- preExecute (perform checks and init)
+  - Check tenant name validity (again)
+    - If name is invalid, throw exception to client; else continue
+- validateAndUpdateCache (update DB)
+  - Grab VOLUME_LOCK write lock
+  - Check volume existence
+    - If tenant already exists, throw exception to client; else continue
+  - Check tenant existence by checking tenantStateTable keys
+    - If tenant already exists, throw exception to client; else continue
+  - tenantStateTable: New entry
+    - Key: tenant name. e.g. finance
+    - Value: new OmDBTenantInfo for the tenant
+      - tenantName: finance
+      - bucketNamespaceName: finance
+      - accountNamespaceName: finance
+      - userPolicyGroupName: finance-users
+      - bucketPolicyGroupName: finance-buckets
+  - tenantPolicyTable: Generate default policies for the new tenant
+    - K: finance-Users, V: finance-users-default
+    - K: finance-Buckets, V: finance-buckets-default
+  - Grab USER_LOCK write lock
+  - Create volume finance (See OMVolumeCreateRequest)
+  - Release VOLUME_LOCK write lock
+  - Release USER_LOCK write lock
+  - Queue Ranger policy sync that pushes default policies:
+      OMMultiTenantManager#createTenant
+ */
+
+/**
+ * Handles OMTenantCreate request.
+ */
+public class OMTenantCreateRequest extends OMVolumeRequest {
+  private static final Logger LOG =
+      LoggerFactory.getLogger(OMTenantCreateRequest.class);
+
+  public OMTenantCreateRequest(OMRequest omRequest) {
+    super(omRequest);
+  }
+
+  @Override
+  public OMRequest preExecute(OzoneManager ozoneManager) throws IOException {
+    final CreateTenantRequest request = getOmRequest().getCreateTenantRequest();
+    final String tenantName = request.getTenantName();
+
+    // Check tenantName validity
+    if (tenantName.contains(OzoneConsts.TENANT_NAME_USER_NAME_DELIMITER)) {
+      throw new OMException("Invalid tenant name " + tenantName +
+          ". Tenant name should not contain delimiter.",
+          OMException.ResultCodes.INVALID_VOLUME_NAME);
+    }
+
+    // getUserName returns:
+    // - Kerberos principal when Kerberos security is enabled
+    // - User's login name when security is not enabled
+    // - AWS_ACCESS_KEY_ID if the original request comes from S3 Gateway.
+    //    Not Applicable to TenantCreateRequest.
+    final String owner = getOmRequest().getUserInfo().getUserName();
+    final String volumeName = tenantName;  // TODO: Configurable
+    final VolumeInfo volumeInfo = VolumeInfo.newBuilder()
+        .setVolume(volumeName)
+        .setAdminName(owner)
+        .setOwnerName(owner)
+        .build();
+    // Verify volume name
+    OmUtils.validateVolumeName(volumeInfo.getVolume());
+
+    // Generate volume modification time
+    long initialTime = Time.now();
+    final VolumeInfo updatedVolumeInfo = volumeInfo.toBuilder()
+            .setCreationTime(initialTime)
+            .setModificationTime(initialTime)
+            .build();
+
+    final OMRequest.Builder omRequestBuilder = getOmRequest().toBuilder()
+        .setCreateTenantRequest(
+            CreateTenantRequest.newBuilder().setTenantName(tenantName))
+        .setCreateVolumeRequest(
+            CreateVolumeRequest.newBuilder().setVolumeInfo(updatedVolumeInfo))
+        .setUserInfo(getUserInfo())
+        .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;
+    OMResponse.Builder omResponse = OmResponseUtil.getOMResponseBuilder(
+        getOmRequest());
+    OmVolumeArgs omVolumeArgs;
+    boolean acquiredVolumeLock = false, acquiredUserLock = false;
+    Tenant tenant = null;
+    final String owner = getOmRequest().getUserInfo().getUserName();
+    LOG.info("owner: {}", owner);  // TODO: owner should be access_key_id
+    Map<String, String> auditMap = new HashMap<>();
+    OMMetadataManager omMetadataManager = ozoneManager.getMetadataManager();
+    CreateTenantRequest request = getOmRequest().getCreateTenantRequest();
+    final String tenantName = request.getTenantName();
+    final VolumeInfo volumeInfo =
+        getOmRequest().getCreateVolumeRequest().getVolumeInfo();
+    final String volumeName = volumeInfo.getVolume();
+    final String dbVolumeKey = omMetadataManager.getVolumeKey(volumeName);
+    IOException exception = null;
+    try {
+      // Check ACL: requires volume create permission. TODO: tenant create perm?

Review comment:
       Can any user who has volume create permissions on Ranger (or admin) be allowed to create a tenant?

##########
File path: hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantCreateRequest.java
##########
@@ -0,0 +1,300 @@
+/*
+ * 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.OmUtils;
+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.OmDBTenantInfo;
+import org.apache.hadoop.ozone.om.helpers.OmVolumeArgs;
+import org.apache.hadoop.ozone.om.multitenant.Tenant;
+import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerDoubleBufferHelper;
+import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
+import org.apache.hadoop.ozone.om.request.volume.OMVolumeRequest;
+import org.apache.hadoop.ozone.om.response.OMClientResponse;
+import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantCreateResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateVolumeRequest;
+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.VolumeInfo;
+import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer;
+import org.apache.hadoop.ozone.security.acl.OzoneObj;
+import org.apache.hadoop.ozone.storage.proto.OzoneManagerStorageProtos.PersistedUserVolumeInfo;
+import org.apache.hadoop.util.Time;
+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.USER_LOCK;
+import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_LOCK;
+
+/*
+  Ratis execution flow for OMTenantCreate
+
+- preExecute (perform checks and init)
+  - Check tenant name validity (again)
+    - If name is invalid, throw exception to client; else continue
+- validateAndUpdateCache (update DB)
+  - Grab VOLUME_LOCK write lock
+  - Check volume existence
+    - If tenant already exists, throw exception to client; else continue
+  - Check tenant existence by checking tenantStateTable keys
+    - If tenant already exists, throw exception to client; else continue
+  - tenantStateTable: New entry
+    - Key: tenant name. e.g. finance
+    - Value: new OmDBTenantInfo for the tenant
+      - tenantName: finance
+      - bucketNamespaceName: finance
+      - accountNamespaceName: finance
+      - userPolicyGroupName: finance-users
+      - bucketPolicyGroupName: finance-buckets
+  - tenantPolicyTable: Generate default policies for the new tenant
+    - K: finance-Users, V: finance-users-default
+    - K: finance-Buckets, V: finance-buckets-default
+  - Grab USER_LOCK write lock
+  - Create volume finance (See OMVolumeCreateRequest)
+  - Release VOLUME_LOCK write lock
+  - Release USER_LOCK write lock
+  - Queue Ranger policy sync that pushes default policies:
+      OMMultiTenantManager#createTenant
+ */
+
+/**
+ * Handles OMTenantCreate request.
+ */
+public class OMTenantCreateRequest extends OMVolumeRequest {
+  private static final Logger LOG =
+      LoggerFactory.getLogger(OMTenantCreateRequest.class);
+
+  public OMTenantCreateRequest(OMRequest omRequest) {
+    super(omRequest);
+  }
+
+  @Override
+  public OMRequest preExecute(OzoneManager ozoneManager) throws IOException {
+    final CreateTenantRequest request = getOmRequest().getCreateTenantRequest();
+    final String tenantName = request.getTenantName();
+
+    // Check tenantName validity
+    if (tenantName.contains(OzoneConsts.TENANT_NAME_USER_NAME_DELIMITER)) {
+      throw new OMException("Invalid tenant name " + tenantName +
+          ". Tenant name should not contain delimiter.",
+          OMException.ResultCodes.INVALID_VOLUME_NAME);
+    }
+
+    // getUserName returns:
+    // - Kerberos principal when Kerberos security is enabled
+    // - User's login name when security is not enabled
+    // - AWS_ACCESS_KEY_ID if the original request comes from S3 Gateway.
+    //    Not Applicable to TenantCreateRequest.
+    final String owner = getOmRequest().getUserInfo().getUserName();
+    final String volumeName = tenantName;  // TODO: Configurable
+    final VolumeInfo volumeInfo = VolumeInfo.newBuilder()
+        .setVolume(volumeName)
+        .setAdminName(owner)
+        .setOwnerName(owner)
+        .build();
+    // Verify volume name
+    OmUtils.validateVolumeName(volumeInfo.getVolume());
+
+    // Generate volume modification time
+    long initialTime = Time.now();
+    final VolumeInfo updatedVolumeInfo = volumeInfo.toBuilder()
+            .setCreationTime(initialTime)
+            .setModificationTime(initialTime)
+            .build();
+
+    final OMRequest.Builder omRequestBuilder = getOmRequest().toBuilder()
+        .setCreateTenantRequest(
+            CreateTenantRequest.newBuilder().setTenantName(tenantName))
+        .setCreateVolumeRequest(
+            CreateVolumeRequest.newBuilder().setVolumeInfo(updatedVolumeInfo))
+        .setUserInfo(getUserInfo())
+        .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;
+    OMResponse.Builder omResponse = OmResponseUtil.getOMResponseBuilder(
+        getOmRequest());
+    OmVolumeArgs omVolumeArgs;
+    boolean acquiredVolumeLock = false, acquiredUserLock = false;
+    Tenant tenant = null;
+    final String owner = getOmRequest().getUserInfo().getUserName();
+    LOG.info("owner: {}", owner);  // TODO: owner should be access_key_id
+    Map<String, String> auditMap = new HashMap<>();
+    OMMetadataManager omMetadataManager = ozoneManager.getMetadataManager();
+    CreateTenantRequest request = getOmRequest().getCreateTenantRequest();
+    final String tenantName = request.getTenantName();
+    final VolumeInfo volumeInfo =
+        getOmRequest().getCreateVolumeRequest().getVolumeInfo();
+    final String volumeName = volumeInfo.getVolume();
+    final String dbVolumeKey = omMetadataManager.getVolumeKey(volumeName);
+    IOException exception = null;
+    try {
+      // Check ACL: requires volume create permission. TODO: tenant create perm?
+      if (ozoneManager.getAclsEnabled()) {
+        checkAcls(ozoneManager, OzoneObj.ResourceType.VOLUME,
+            OzoneObj.StoreType.OZONE, IAccessAuthorizer.ACLType.CREATE,
+            tenantName, null, null);
+      }
+
+      acquiredVolumeLock = omMetadataManager.getLock().acquireWriteLock(
+          VOLUME_LOCK, volumeName);
+      // Check volume existence
+      if (omMetadataManager.getVolumeTable().isExist(volumeName)) {
+        LOG.debug("volume: {} already exists", volumeName);
+        throw new OMException("Volume already exists",
+            OMException.ResultCodes.VOLUME_ALREADY_EXISTS);
+      }
+      // Check tenant existence in tenantStateTable
+      if (omMetadataManager.getTenantStateTable().isExist(tenantName)) {
+        LOG.debug("tenant: {} already exists", tenantName);
+        throw new OMException("Tenant already exists",
+            OMException.ResultCodes.TENANT_ALREADY_EXISTS);
+      }
+
+      // Add to tenantStateTable. Redundant assignment for clarity
+      final String bucketNamespaceName = tenantName;
+      final String accountNamespaceName = tenantName;
+      final String userPolicyGroupName =
+          tenantName + OzoneConsts.DEFAULT_TENANT_USER_POLICY_SUFFIX;
+      final String bucketPolicyGroupName =
+          tenantName + OzoneConsts.DEFAULT_TENANT_BUCKET_POLICY_SUFFIX;
+      final OmDBTenantInfo omDBTenantInfo = new OmDBTenantInfo(
+          tenantName, bucketNamespaceName, accountNamespaceName,
+          userPolicyGroupName, bucketPolicyGroupName);
+      omMetadataManager.getTenantStateTable().addCacheEntry(
+          new CacheKey<>(tenantName),
+          new CacheValue<>(Optional.of(omDBTenantInfo), transactionLogIndex));
+
+      // Call OMMultiTenantManager
+//      tenant = ozoneManager.getMultiTenantManager().createTenant(tenantName);

Review comment:
       This would be a no go in actual (non prototype) code. We are making a Ranger call with volume lock, and from inside Ratis applyTxn. 

##########
File path: hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantCreateRequest.java
##########
@@ -0,0 +1,300 @@
+/*
+ * 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.OmUtils;
+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.OmDBTenantInfo;
+import org.apache.hadoop.ozone.om.helpers.OmVolumeArgs;
+import org.apache.hadoop.ozone.om.multitenant.Tenant;
+import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerDoubleBufferHelper;
+import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
+import org.apache.hadoop.ozone.om.request.volume.OMVolumeRequest;
+import org.apache.hadoop.ozone.om.response.OMClientResponse;
+import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantCreateResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateVolumeRequest;
+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.VolumeInfo;
+import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer;
+import org.apache.hadoop.ozone.security.acl.OzoneObj;
+import org.apache.hadoop.ozone.storage.proto.OzoneManagerStorageProtos.PersistedUserVolumeInfo;
+import org.apache.hadoop.util.Time;
+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.USER_LOCK;
+import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_LOCK;
+
+/*
+  Ratis execution flow for OMTenantCreate
+
+- preExecute (perform checks and init)
+  - Check tenant name validity (again)
+    - If name is invalid, throw exception to client; else continue
+- validateAndUpdateCache (update DB)
+  - Grab VOLUME_LOCK write lock
+  - Check volume existence
+    - If tenant already exists, throw exception to client; else continue
+  - Check tenant existence by checking tenantStateTable keys
+    - If tenant already exists, throw exception to client; else continue
+  - tenantStateTable: New entry
+    - Key: tenant name. e.g. finance
+    - Value: new OmDBTenantInfo for the tenant
+      - tenantName: finance
+      - bucketNamespaceName: finance
+      - accountNamespaceName: finance
+      - userPolicyGroupName: finance-users
+      - bucketPolicyGroupName: finance-buckets
+  - tenantPolicyTable: Generate default policies for the new tenant
+    - K: finance-Users, V: finance-users-default
+    - K: finance-Buckets, V: finance-buckets-default
+  - Grab USER_LOCK write lock
+  - Create volume finance (See OMVolumeCreateRequest)
+  - Release VOLUME_LOCK write lock
+  - Release USER_LOCK write lock
+  - Queue Ranger policy sync that pushes default policies:
+      OMMultiTenantManager#createTenant
+ */
+
+/**
+ * Handles OMTenantCreate request.
+ */
+public class OMTenantCreateRequest extends OMVolumeRequest {
+  private static final Logger LOG =
+      LoggerFactory.getLogger(OMTenantCreateRequest.class);
+
+  public OMTenantCreateRequest(OMRequest omRequest) {
+    super(omRequest);
+  }
+
+  @Override
+  public OMRequest preExecute(OzoneManager ozoneManager) throws IOException {
+    final CreateTenantRequest request = getOmRequest().getCreateTenantRequest();
+    final String tenantName = request.getTenantName();
+
+    // Check tenantName validity
+    if (tenantName.contains(OzoneConsts.TENANT_NAME_USER_NAME_DELIMITER)) {
+      throw new OMException("Invalid tenant name " + tenantName +
+          ". Tenant name should not contain delimiter.",
+          OMException.ResultCodes.INVALID_VOLUME_NAME);
+    }
+
+    // getUserName returns:
+    // - Kerberos principal when Kerberos security is enabled
+    // - User's login name when security is not enabled
+    // - AWS_ACCESS_KEY_ID if the original request comes from S3 Gateway.
+    //    Not Applicable to TenantCreateRequest.
+    final String owner = getOmRequest().getUserInfo().getUserName();
+    final String volumeName = tenantName;  // TODO: Configurable
+    final VolumeInfo volumeInfo = VolumeInfo.newBuilder()
+        .setVolume(volumeName)
+        .setAdminName(owner)
+        .setOwnerName(owner)
+        .build();
+    // Verify volume name
+    OmUtils.validateVolumeName(volumeInfo.getVolume());
+
+    // Generate volume modification time
+    long initialTime = Time.now();
+    final VolumeInfo updatedVolumeInfo = volumeInfo.toBuilder()
+            .setCreationTime(initialTime)
+            .setModificationTime(initialTime)
+            .build();
+
+    final OMRequest.Builder omRequestBuilder = getOmRequest().toBuilder()
+        .setCreateTenantRequest(
+            CreateTenantRequest.newBuilder().setTenantName(tenantName))
+        .setCreateVolumeRequest(
+            CreateVolumeRequest.newBuilder().setVolumeInfo(updatedVolumeInfo))
+        .setUserInfo(getUserInfo())
+        .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;
+    OMResponse.Builder omResponse = OmResponseUtil.getOMResponseBuilder(
+        getOmRequest());
+    OmVolumeArgs omVolumeArgs;
+    boolean acquiredVolumeLock = false, acquiredUserLock = false;
+    Tenant tenant = null;
+    final String owner = getOmRequest().getUserInfo().getUserName();
+    LOG.info("owner: {}", owner);  // TODO: owner should be access_key_id
+    Map<String, String> auditMap = new HashMap<>();
+    OMMetadataManager omMetadataManager = ozoneManager.getMetadataManager();
+    CreateTenantRequest request = getOmRequest().getCreateTenantRequest();
+    final String tenantName = request.getTenantName();
+    final VolumeInfo volumeInfo =
+        getOmRequest().getCreateVolumeRequest().getVolumeInfo();
+    final String volumeName = volumeInfo.getVolume();
+    final String dbVolumeKey = omMetadataManager.getVolumeKey(volumeName);
+    IOException exception = null;
+    try {
+      // Check ACL: requires volume create permission. TODO: tenant create perm?
+      if (ozoneManager.getAclsEnabled()) {
+        checkAcls(ozoneManager, OzoneObj.ResourceType.VOLUME,
+            OzoneObj.StoreType.OZONE, IAccessAuthorizer.ACLType.CREATE,
+            tenantName, null, null);
+      }
+
+      acquiredVolumeLock = omMetadataManager.getLock().acquireWriteLock(
+          VOLUME_LOCK, volumeName);
+      // Check volume existence
+      if (omMetadataManager.getVolumeTable().isExist(volumeName)) {
+        LOG.debug("volume: {} already exists", volumeName);
+        throw new OMException("Volume already exists",
+            OMException.ResultCodes.VOLUME_ALREADY_EXISTS);
+      }
+      // Check tenant existence in tenantStateTable
+      if (omMetadataManager.getTenantStateTable().isExist(tenantName)) {
+        LOG.debug("tenant: {} already exists", tenantName);
+        throw new OMException("Tenant already exists",
+            OMException.ResultCodes.TENANT_ALREADY_EXISTS);
+      }
+
+      // Add to tenantStateTable. Redundant assignment for clarity
+      final String bucketNamespaceName = tenantName;
+      final String accountNamespaceName = tenantName;
+      final String userPolicyGroupName =
+          tenantName + OzoneConsts.DEFAULT_TENANT_USER_POLICY_SUFFIX;
+      final String bucketPolicyGroupName =
+          tenantName + OzoneConsts.DEFAULT_TENANT_BUCKET_POLICY_SUFFIX;
+      final OmDBTenantInfo omDBTenantInfo = new OmDBTenantInfo(
+          tenantName, bucketNamespaceName, accountNamespaceName,
+          userPolicyGroupName, bucketPolicyGroupName);
+      omMetadataManager.getTenantStateTable().addCacheEntry(
+          new CacheKey<>(tenantName),
+          new CacheValue<>(Optional.of(omDBTenantInfo), transactionLogIndex));
+
+      // Call OMMultiTenantManager
+//      tenant = ozoneManager.getMultiTenantManager().createTenant(tenantName);

Review comment:
       We can have a policy sync queue (and persistence) that pushes the policies to the external plugin in an async manner.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org


[GitHub] [ozone] prashantpogde commented on a change in pull request #2059: HDDS-4945. Initial prototype for MultiTenant support for Ozone.

Posted by GitBox <gi...@apache.org>.
prashantpogde commented on a change in pull request #2059:
URL: https://github.com/apache/ozone/pull/2059#discussion_r617138568



##########
File path: hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantCreateRequest.java
##########
@@ -0,0 +1,300 @@
+/*
+ * 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.OmUtils;
+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.OmDBTenantInfo;
+import org.apache.hadoop.ozone.om.helpers.OmVolumeArgs;
+import org.apache.hadoop.ozone.om.multitenant.Tenant;
+import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerDoubleBufferHelper;
+import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
+import org.apache.hadoop.ozone.om.request.volume.OMVolumeRequest;
+import org.apache.hadoop.ozone.om.response.OMClientResponse;
+import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantCreateResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateVolumeRequest;
+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.VolumeInfo;
+import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer;
+import org.apache.hadoop.ozone.security.acl.OzoneObj;
+import org.apache.hadoop.ozone.storage.proto.OzoneManagerStorageProtos.PersistedUserVolumeInfo;
+import org.apache.hadoop.util.Time;
+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.USER_LOCK;
+import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_LOCK;
+
+/*
+  Ratis execution flow for OMTenantCreate
+
+- preExecute (perform checks and init)
+  - Check tenant name validity (again)
+    - If name is invalid, throw exception to client; else continue
+- validateAndUpdateCache (update DB)
+  - Grab VOLUME_LOCK write lock
+  - Check volume existence
+    - If tenant already exists, throw exception to client; else continue
+  - Check tenant existence by checking tenantStateTable keys
+    - If tenant already exists, throw exception to client; else continue
+  - tenantStateTable: New entry
+    - Key: tenant name. e.g. finance
+    - Value: new OmDBTenantInfo for the tenant
+      - tenantName: finance
+      - bucketNamespaceName: finance
+      - accountNamespaceName: finance
+      - userPolicyGroupName: finance-users
+      - bucketPolicyGroupName: finance-buckets
+  - tenantPolicyTable: Generate default policies for the new tenant
+    - K: finance-Users, V: finance-users-default
+    - K: finance-Buckets, V: finance-buckets-default
+  - Grab USER_LOCK write lock
+  - Create volume finance (See OMVolumeCreateRequest)
+  - Release VOLUME_LOCK write lock
+  - Release USER_LOCK write lock
+  - Queue Ranger policy sync that pushes default policies:
+      OMMultiTenantManager#createTenant
+ */
+
+/**
+ * Handles OMTenantCreate request.
+ */
+public class OMTenantCreateRequest extends OMVolumeRequest {
+  private static final Logger LOG =
+      LoggerFactory.getLogger(OMTenantCreateRequest.class);
+
+  public OMTenantCreateRequest(OMRequest omRequest) {
+    super(omRequest);
+  }
+
+  @Override
+  public OMRequest preExecute(OzoneManager ozoneManager) throws IOException {
+    final CreateTenantRequest request = getOmRequest().getCreateTenantRequest();
+    final String tenantName = request.getTenantName();
+
+    // Check tenantName validity
+    if (tenantName.contains(OzoneConsts.TENANT_NAME_USER_NAME_DELIMITER)) {
+      throw new OMException("Invalid tenant name " + tenantName +
+          ". Tenant name should not contain delimiter.",
+          OMException.ResultCodes.INVALID_VOLUME_NAME);
+    }
+
+    // getUserName returns:
+    // - Kerberos principal when Kerberos security is enabled
+    // - User's login name when security is not enabled
+    // - AWS_ACCESS_KEY_ID if the original request comes from S3 Gateway.
+    //    Not Applicable to TenantCreateRequest.
+    final String owner = getOmRequest().getUserInfo().getUserName();
+    final String volumeName = tenantName;  // TODO: Configurable
+    final VolumeInfo volumeInfo = VolumeInfo.newBuilder()
+        .setVolume(volumeName)
+        .setAdminName(owner)
+        .setOwnerName(owner)
+        .build();
+    // Verify volume name
+    OmUtils.validateVolumeName(volumeInfo.getVolume());
+
+    // Generate volume modification time
+    long initialTime = Time.now();
+    final VolumeInfo updatedVolumeInfo = volumeInfo.toBuilder()
+            .setCreationTime(initialTime)
+            .setModificationTime(initialTime)
+            .build();
+
+    final OMRequest.Builder omRequestBuilder = getOmRequest().toBuilder()
+        .setCreateTenantRequest(
+            CreateTenantRequest.newBuilder().setTenantName(tenantName))
+        .setCreateVolumeRequest(
+            CreateVolumeRequest.newBuilder().setVolumeInfo(updatedVolumeInfo))
+        .setUserInfo(getUserInfo())
+        .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;
+    OMResponse.Builder omResponse = OmResponseUtil.getOMResponseBuilder(
+        getOmRequest());
+    OmVolumeArgs omVolumeArgs;
+    boolean acquiredVolumeLock = false, acquiredUserLock = false;
+    Tenant tenant = null;
+    final String owner = getOmRequest().getUserInfo().getUserName();
+    LOG.info("owner: {}", owner);  // TODO: owner should be access_key_id
+    Map<String, String> auditMap = new HashMap<>();
+    OMMetadataManager omMetadataManager = ozoneManager.getMetadataManager();
+    CreateTenantRequest request = getOmRequest().getCreateTenantRequest();
+    final String tenantName = request.getTenantName();
+    final VolumeInfo volumeInfo =
+        getOmRequest().getCreateVolumeRequest().getVolumeInfo();
+    final String volumeName = volumeInfo.getVolume();
+    final String dbVolumeKey = omMetadataManager.getVolumeKey(volumeName);
+    IOException exception = null;
+    try {
+      // Check ACL: requires volume create permission. TODO: tenant create perm?
+      if (ozoneManager.getAclsEnabled()) {
+        checkAcls(ozoneManager, OzoneObj.ResourceType.VOLUME,
+            OzoneObj.StoreType.OZONE, IAccessAuthorizer.ACLType.CREATE,
+            tenantName, null, null);
+      }
+
+      acquiredVolumeLock = omMetadataManager.getLock().acquireWriteLock(
+          VOLUME_LOCK, volumeName);
+      // Check volume existence
+      if (omMetadataManager.getVolumeTable().isExist(volumeName)) {
+        LOG.debug("volume: {} already exists", volumeName);
+        throw new OMException("Volume already exists",
+            OMException.ResultCodes.VOLUME_ALREADY_EXISTS);
+      }
+      // Check tenant existence in tenantStateTable
+      if (omMetadataManager.getTenantStateTable().isExist(tenantName)) {
+        LOG.debug("tenant: {} already exists", tenantName);
+        throw new OMException("Tenant already exists",
+            OMException.ResultCodes.TENANT_ALREADY_EXISTS);
+      }
+
+      // Add to tenantStateTable. Redundant assignment for clarity
+      final String bucketNamespaceName = tenantName;
+      final String accountNamespaceName = tenantName;
+      final String userPolicyGroupName =
+          tenantName + OzoneConsts.DEFAULT_TENANT_USER_POLICY_SUFFIX;
+      final String bucketPolicyGroupName =
+          tenantName + OzoneConsts.DEFAULT_TENANT_BUCKET_POLICY_SUFFIX;
+      final OmDBTenantInfo omDBTenantInfo = new OmDBTenantInfo(
+          tenantName, bucketNamespaceName, accountNamespaceName,
+          userPolicyGroupName, bucketPolicyGroupName);
+      omMetadataManager.getTenantStateTable().addCacheEntry(
+          new CacheKey<>(tenantName),
+          new CacheValue<>(Optional.of(omDBTenantInfo), transactionLogIndex));
+
+      // Call OMMultiTenantManager
+//      tenant = ozoneManager.getMultiTenantManager().createTenant(tenantName);

Review comment:
       Thanks for summarizing the discussion @smengcl.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org


[GitHub] [ozone] avijayanhwx commented on a change in pull request #2059: HDDS-4945. Initial prototype for MultiTenant support for Ozone.

Posted by GitBox <gi...@apache.org>.
avijayanhwx commented on a change in pull request #2059:
URL: https://github.com/apache/ozone/pull/2059#discussion_r598773473



##########
File path: hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/AccessPolicy.java
##########
@@ -0,0 +1,32 @@
+/**
+ * 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.multitenant;
+
+import java.security.Principal;
+
+import org.apache.hadoop.hdds.annotation.InterfaceAudience;
+import org.apache.hadoop.hdds.annotation.InterfaceStability;
+
+@InterfaceAudience.LimitedPrivate({"HDFS", "Yarn", "Ranger", "Hive", "HBase"})
+@InterfaceStability.Evolving
+public interface AccessPolicy {
+
+  void createPolicy(String policyJsonString);

Review comment:
       As discussed offline, can we find a way to not use JSON argument in the interface contract? 

##########
File path: hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/MultiTenantGateKeeperRangerPlugin.java
##########
@@ -0,0 +1,358 @@
+package org.apache.hadoop.ozone.om.multitenant;
+
+import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ADMIN_CREATE_GROUP_HTTP_ENDPOINT;
+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_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.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.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_OM_CONNECTION_REQUEST_TIMEOUT;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_OM_CONNECTION_REQUEST_TIMEOUT_DEFAULT;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_OM_CONNECTION_TIMEOUT;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_OM_CONNECTION_TIMEOUT_DEFAULT;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_OM_IGNORE_SERVER_CERT;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_OM_IGNORE_SERVER_CERT_DEFAULT;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+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;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.commons.net.util.Base64;
+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.codehaus.jettison.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.oracle.javafx.jmx.json.JSONException;
+
+import javafx.util.Pair;
+
+public class MultiTenantGateKeeperRangerPlugin implements
+    MultiTenantGateKeeper {
+  private static final Logger LOG = LoggerFactory
+      .getLogger(MultiTenantGateKeeperRangerPlugin.class);
+
+  private static OzoneConfiguration conf;
+  private static boolean ignoreServerCert = false;
+  private static int connectionTimeout;
+  private static int connectionRequestTimeout;
+  private static String authHeaderValue;
+
+  @Override
+  public void init(Configuration configuration) throws IOException {
+    conf = (OzoneConfiguration)configuration;
+    initializeRangerConnection();
+  }
+
+  private void initializeRangerConnection() {
+    setupRangerConnectionConfig();
+    if (ignoreServerCert) {
+      setupRangerIgnoreServerCertificate();
+    }
+    setupRangerConnectionAuthHeader();
+  }
+
+  private void setupRangerConnectionConfig() {
+    connectionTimeout = (int) conf.getTimeDuration(
+        OZONE_RANGER_OM_CONNECTION_TIMEOUT,
+        conf.get(
+            OZONE_RANGER_OM_CONNECTION_TIMEOUT,
+            OZONE_RANGER_OM_CONNECTION_TIMEOUT_DEFAULT),
+        TimeUnit.MILLISECONDS);
+    connectionRequestTimeout = (int)conf.getTimeDuration(
+        OZONE_RANGER_OM_CONNECTION_REQUEST_TIMEOUT,
+        conf.get(
+            OZONE_RANGER_OM_CONNECTION_REQUEST_TIMEOUT,
+            OZONE_RANGER_OM_CONNECTION_REQUEST_TIMEOUT_DEFAULT),
+        TimeUnit.MILLISECONDS
+    );
+    ignoreServerCert = (boolean) conf.getBoolean(
+        OZONE_RANGER_OM_IGNORE_SERVER_CERT,
+            OZONE_RANGER_OM_IGNORE_SERVER_CERT_DEFAULT);
+  }
+
+  private void setupRangerIgnoreServerCertificate() {
+    // Create a trust manager that does not validate certificate chains
+    TrustManager[] trustAllCerts = new TrustManager[]{
+        new X509TrustManager() {
+          public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+            return null;
+          }
+          public void checkClientTrusted(
+              java.security.cert.X509Certificate[] certs, String authType) {
+          }
+          public void checkServerTrusted(
+              java.security.cert.X509Certificate[] certs, String authType) {
+          }
+        }
+    };
+
+    try {
+      SSLContext sc = SSLContext.getInstance("SSL");
+      sc.init(null, trustAllCerts, new java.security.SecureRandom());
+      HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
+    } catch (Exception e) {
+      LOG.info("Setting DefaultSSLSocketFactory failed.");
+    }
+  }
+
+  private void setupRangerConnectionAuthHeader() {
+    String userName = conf.get(OZONE_OM_RANGER_HTTPS_ADMIN_API_USER);
+    String passwd = conf.get(OZONE_OM_RANGER_HTTPS_ADMIN_API_PASSWD);
+    String auth = userName + ":" + passwd;
+    byte[] encodedAuth =
+        Base64.encodeBase64(auth.getBytes(StandardCharsets.UTF_8));
+    authHeaderValue = "Basic " + new String(encodedAuth);
+  }
+
+
+  @Override
+  public void shutdown() throws Exception {
+    // TBD
+  }
+
+  @Override
+  public void grantAccess(BucketNameSpace bucketNameSpace,
+                          OzoneMultiTenantPrincipal user, ACLType aclType) {
+    // TBD
+  }
+
+  @Override
+  public void revokeAccess(BucketNameSpace bucketNameSpace,
+                           OzoneMultiTenantPrincipal user, ACLType aclType) {
+    // TBD
+  }
+
+  @Override
+  public void grantAccess(AccountNameSpace accountNameSpace,
+                          OzoneMultiTenantPrincipal user, ACLType aclType) {
+    // TBD
+  }
+
+  @Override
+  public void revokeAccess(AccountNameSpace accountNameSpace,
+                           OzoneMultiTenantPrincipal user, ACLType aclType) {
+    // TBD
+  }
+
+  @Override
+  public List<Pair<BucketNameSpace, ACLType>>
+  getAllBucketNameSpaceAccessess(OzoneMultiTenantPrincipal user) {
+    // TBD
+    return null;
+  }
+
+  @Override
+  public boolean checkAccess(BucketNameSpace bucketNameSpace,
+                             OzoneMultiTenantPrincipal user) {
+    // TBD
+    return true;
+  }
+
+  @Override
+  public boolean checkAccess(AccountNameSpace accountNameSpace,
+                             OzoneMultiTenantPrincipal user) {
+    // TBD
+    return true;
+  }
+
+  @Override
+  public boolean checkAccess(IOzoneObj ozoneObject, RequestContext context)
+      throws OMException {
+    // 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;
+  }
+
+  public String createUser(String userName, List<String> groupIDs)
+      throws Exception {
+    String rangerHttpsAddress = conf.get(OZONE_RANGER_HTTPS_ADDRESS_KEY);
+    String rangerAdminUrl =
+        rangerHttpsAddress + OZONE_OM_RANGER_ADMIN_CREATE_USER_HTTP_ENDPOINT;
+
+    String jsonCreateUserString = getCreateUserJsonString(userName, groupIDs);
+
+    HttpsURLConnection conn = makeHttpsPostCall(rangerAdminUrl,
+        jsonCreateUserString,"POST", false);
+    String userInfo = getReponseData(conn);
+    String userIDCreated;
+    try {
+      JSONObject jObject = new JSONObject(userInfo.toString());
+      userIDCreated = jObject.getString("id");
+      System.out.println("User ID is : " + userIDCreated);
+    } catch (JSONException e) {
+      e.printStackTrace();
+      throw e;
+    }
+    return userIDCreated;
+  }
+
+  private String getCreateGroupJsonString(String groupName) throws Exception {
+    String jsonCreateGroupString = "{ \"name\":\"" + groupName + "\"," +
+        "  \"description\":\"test\" " +
+        " }";
+    return jsonCreateGroupString;
+  }
+
+  public String createGroup(String groupName) throws Exception {
+    String rangerHttpsAddress = conf.get(OZONE_RANGER_HTTPS_ADDRESS_KEY);

Review comment:
       nit. Can be a class variable.

##########
File path: hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManager.java
##########
@@ -0,0 +1,237 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership.  The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.hadoop.ozone.om;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.ozone.om.multitenant.AccessPolicy;
+import org.apache.hadoop.ozone.om.multitenant.AccountNameSpace;
+import org.apache.hadoop.ozone.om.multitenant.BucketNameSpace;
+import org.apache.hadoop.ozone.om.multitenant.OzoneMultiTenantPrincipal;
+import org.apache.hadoop.ozone.om.multitenant.Tenant;
+
+import javafx.util.Pair;
+
+/**
+ * OM MultiTenant manager interface.
+ */
+public interface OMMultiTenantManager {
+  /**
+   * Start multi-tenant manager. Performs initialization e.g.
+   *  - Initialize Multi-Tenant-Gatekeeper-Plugin
+   *  - Validate Multi-Tenant Bucket-NameSpaces
+   *  - Validate Multi-Tenant Account-NameSpaces
+   *  - Validating various OM (Multi-Tenant state)tables and corresponding
+   *    state in IMultiTenantGateKeeperPlugin (Ranger/Native/AnyOtherPlugIn).
+   *  - Setup SuperUsers for Multi-Tenant environment from Ozone-Conf
+   *  - Periodic BackGround thread to keep MultiTenant-State consistent e.g.
+   *       . superusers  <-in-sync-> OzoneConf,
+   *       . OM-DB state <-in-sync-> IMultiTenantGateKeeperPluginState
+   *       . OM DB state is always the source of truth.
+   *
+   * @param configuration
+   * @throws IOException
+   */
+  void start(OzoneConfiguration configuration) throws IOException;
+
+  /**
+   * Stop multi-tenant manager.
+   */
+  void stop() throws Exception;
+
+  /**
+   * Returns the corresponding OzoneManager instance.
+   *
+   * @return OMMetadataManager
+   */
+  OMMetadataManager getOzoneManager();
+
+  /**
+   * Given a TenantID String, Create and return Tenant Interface.
+   *
+   * @param tenantID
+   * @return Tenant interface.
+   */
+  Tenant createTenant(String tenantID) throws IOException;

Review comment:
       As discussed offline, all write operations defined here need to be integrated with the OM ratis request handling framework.

##########
File path: hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/BucketNameSpace.java
##########
@@ -0,0 +1,37 @@
+package org.apache.hadoop.ozone.om.multitenant;
+
+import java.util.List;
+
+import org.apache.hadoop.hdds.annotation.InterfaceAudience;
+import org.apache.hadoop.hdds.annotation.InterfaceStability;
+import org.apache.hadoop.ozone.security.acl.OzoneObj;
+
+@InterfaceAudience.LimitedPrivate({"HDFS", "Yarn", "Ranger", "Hive", "HBase"})
+@InterfaceStability.Evolving
+public interface BucketNameSpace {

Review comment:
       Same doc request here.

##########
File path: hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/AccountNameSpace.java
##########
@@ -0,0 +1,21 @@
+package org.apache.hadoop.ozone.om.multitenant;
+
+
+import org.apache.hadoop.hdds.annotation.InterfaceAudience;
+import org.apache.hadoop.hdds.annotation.InterfaceStability;
+
+@InterfaceAudience.LimitedPrivate({"HDFS", "Yarn", "Ranger", "Hive", "HBase"})
+@InterfaceStability.Evolving
+
+public interface AccountNameSpace {

Review comment:
       Can we write some documentation on what constitutes an Account Namespace here? It will be a useful reference in the future. And this interface only seems to have a getter & setter?

##########
File path: hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantGateKeeperRangerPlugin.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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.hadoop.ozone.om.multitenant;
+
+import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ADMIN_CREATE_GROUP_HTTP_ENDPOINT;
+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_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.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.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_OM_CONNECTION_REQUEST_TIMEOUT;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_OM_CONNECTION_REQUEST_TIMEOUT_DEFAULT;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_OM_CONNECTION_TIMEOUT;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_OM_CONNECTION_TIMEOUT_DEFAULT;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.apache.commons.net.util.Base64;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.security.authentication.client.AuthenticationException;
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.Timeout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Tests TestMultiTenantGateKeeperImplWithRanger.
+ * Marking it as Ignore because it needs Ranger access point.
+ */
+public class TestMultiTenantGateKeeperRangerPlugin {
+  private static final Logger LOG = LoggerFactory
+      .getLogger(TestMultiTenantGateKeeperRangerPlugin.class);
+
+  /**
+   * Set a timeout for each test.
+   */
+  @Rule
+  public Timeout timeout = new Timeout(300000);
+
+  // The following values need to be set before this test can be enabled.
+  private static final String RANGER_ENDPOINT =
+      "https://s3-tenant-1.s3-tenant.root.hwx.site:6182";

Review comment:
       Can we remove this internal host detail?

##########
File path: hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManager.java
##########
@@ -0,0 +1,237 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership.  The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.hadoop.ozone.om;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.ozone.om.multitenant.AccessPolicy;
+import org.apache.hadoop.ozone.om.multitenant.AccountNameSpace;
+import org.apache.hadoop.ozone.om.multitenant.BucketNameSpace;
+import org.apache.hadoop.ozone.om.multitenant.OzoneMultiTenantPrincipal;
+import org.apache.hadoop.ozone.om.multitenant.Tenant;
+
+import javafx.util.Pair;
+
+/**
+ * OM MultiTenant manager interface.
+ */
+public interface OMMultiTenantManager {
+  /**
+   * Start multi-tenant manager. Performs initialization e.g.
+   *  - Initialize Multi-Tenant-Gatekeeper-Plugin
+   *  - Validate Multi-Tenant Bucket-NameSpaces
+   *  - Validate Multi-Tenant Account-NameSpaces
+   *  - Validating various OM (Multi-Tenant state)tables and corresponding
+   *    state in IMultiTenantGateKeeperPlugin (Ranger/Native/AnyOtherPlugIn).
+   *  - Setup SuperUsers for Multi-Tenant environment from Ozone-Conf
+   *  - Periodic BackGround thread to keep MultiTenant-State consistent e.g.
+   *       . superusers  <-in-sync-> OzoneConf,
+   *       . OM-DB state <-in-sync-> IMultiTenantGateKeeperPluginState
+   *       . OM DB state is always the source of truth.
+   *
+   * @param configuration
+   * @throws IOException
+   */
+  void start(OzoneConfiguration configuration) throws IOException;
+
+  /**
+   * Stop multi-tenant manager.
+   */
+  void stop() throws Exception;
+
+  /**
+   * Returns the corresponding OzoneManager instance.
+   *
+   * @return OMMetadataManager
+   */
+  OMMetadataManager getOzoneManager();

Review comment:
       nit. getOmMetadataManager() ?




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org


[GitHub] [ozone] elek commented on pull request #2059: HDDS-4945. Initial prototype for MultiTenant support for Ozone.

Posted by GitBox <gi...@apache.org>.
elek commented on pull request #2059:
URL: https://github.com/apache/ozone/pull/2059#issuecomment-823976635


   > I think @elek's suggestion was exactly the opposite. Please keep the POC (the feature branch HDDS-4944-S3-Multitenant-Users) in a fork until it is stable enough. Otherwise it will unnecessarily use Apache org's Github Actions quota, both for PRs and for keeping the feature branch up-to-date from master.
   
   Yes, agree. I think it's better to create the branch on apache repository when there is a conclusion/agreeent on the design. Based on the understanding we are not there, yet.
   
   Creating poc can be useful to demonstrate any proposed feature, but it can be done on forks without any problems.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org


[GitHub] [ozone] smengcl commented on a change in pull request #2059: HDDS-4945. Initial prototype for MultiTenant support for Ozone.

Posted by GitBox <gi...@apache.org>.
smengcl commented on a change in pull request #2059:
URL: https://github.com/apache/ozone/pull/2059#discussion_r609023425



##########
File path: hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantCreateRequest.java
##########
@@ -0,0 +1,300 @@
+/*
+ * 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.OmUtils;
+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.OmDBTenantInfo;
+import org.apache.hadoop.ozone.om.helpers.OmVolumeArgs;
+import org.apache.hadoop.ozone.om.multitenant.Tenant;
+import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerDoubleBufferHelper;
+import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
+import org.apache.hadoop.ozone.om.request.volume.OMVolumeRequest;
+import org.apache.hadoop.ozone.om.response.OMClientResponse;
+import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantCreateResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateVolumeRequest;
+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.VolumeInfo;
+import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer;
+import org.apache.hadoop.ozone.security.acl.OzoneObj;
+import org.apache.hadoop.ozone.storage.proto.OzoneManagerStorageProtos.PersistedUserVolumeInfo;
+import org.apache.hadoop.util.Time;
+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.USER_LOCK;
+import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_LOCK;
+
+/*
+  Ratis execution flow for OMTenantCreate
+
+- preExecute (perform checks and init)
+  - Check tenant name validity (again)
+    - If name is invalid, throw exception to client; else continue
+- validateAndUpdateCache (update DB)
+  - Grab VOLUME_LOCK write lock
+  - Check volume existence
+    - If tenant already exists, throw exception to client; else continue
+  - Check tenant existence by checking tenantStateTable keys
+    - If tenant already exists, throw exception to client; else continue
+  - tenantStateTable: New entry
+    - Key: tenant name. e.g. finance
+    - Value: new OmDBTenantInfo for the tenant
+      - tenantName: finance
+      - bucketNamespaceName: finance
+      - accountNamespaceName: finance
+      - userPolicyGroupName: finance-users
+      - bucketPolicyGroupName: finance-buckets
+  - tenantPolicyTable: Generate default policies for the new tenant
+    - K: finance-Users, V: finance-users-default
+    - K: finance-Buckets, V: finance-buckets-default
+  - Grab USER_LOCK write lock
+  - Create volume finance (See OMVolumeCreateRequest)
+  - Release VOLUME_LOCK write lock
+  - Release USER_LOCK write lock
+  - Queue Ranger policy sync that pushes default policies:
+      OMMultiTenantManager#createTenant
+ */
+
+/**
+ * Handles OMTenantCreate request.
+ */
+public class OMTenantCreateRequest extends OMVolumeRequest {
+  private static final Logger LOG =
+      LoggerFactory.getLogger(OMTenantCreateRequest.class);
+
+  public OMTenantCreateRequest(OMRequest omRequest) {
+    super(omRequest);
+  }
+
+  @Override
+  public OMRequest preExecute(OzoneManager ozoneManager) throws IOException {
+    final CreateTenantRequest request = getOmRequest().getCreateTenantRequest();
+    final String tenantName = request.getTenantName();
+
+    // Check tenantName validity
+    if (tenantName.contains(OzoneConsts.TENANT_NAME_USER_NAME_DELIMITER)) {
+      throw new OMException("Invalid tenant name " + tenantName +
+          ". Tenant name should not contain delimiter.",
+          OMException.ResultCodes.INVALID_VOLUME_NAME);
+    }
+
+    // getUserName returns:
+    // - Kerberos principal when Kerberos security is enabled
+    // - User's login name when security is not enabled
+    // - AWS_ACCESS_KEY_ID if the original request comes from S3 Gateway.
+    //    Not Applicable to TenantCreateRequest.
+    final String owner = getOmRequest().getUserInfo().getUserName();
+    final String volumeName = tenantName;  // TODO: Configurable
+    final VolumeInfo volumeInfo = VolumeInfo.newBuilder()
+        .setVolume(volumeName)
+        .setAdminName(owner)
+        .setOwnerName(owner)
+        .build();
+    // Verify volume name
+    OmUtils.validateVolumeName(volumeInfo.getVolume());
+
+    // Generate volume modification time
+    long initialTime = Time.now();
+    final VolumeInfo updatedVolumeInfo = volumeInfo.toBuilder()
+            .setCreationTime(initialTime)
+            .setModificationTime(initialTime)
+            .build();
+
+    final OMRequest.Builder omRequestBuilder = getOmRequest().toBuilder()
+        .setCreateTenantRequest(
+            CreateTenantRequest.newBuilder().setTenantName(tenantName))
+        .setCreateVolumeRequest(
+            CreateVolumeRequest.newBuilder().setVolumeInfo(updatedVolumeInfo))
+        .setUserInfo(getUserInfo())
+        .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;
+    OMResponse.Builder omResponse = OmResponseUtil.getOMResponseBuilder(
+        getOmRequest());
+    OmVolumeArgs omVolumeArgs;
+    boolean acquiredVolumeLock = false, acquiredUserLock = false;
+    Tenant tenant = null;
+    final String owner = getOmRequest().getUserInfo().getUserName();
+    LOG.info("owner: {}", owner);  // TODO: owner should be access_key_id
+    Map<String, String> auditMap = new HashMap<>();
+    OMMetadataManager omMetadataManager = ozoneManager.getMetadataManager();
+    CreateTenantRequest request = getOmRequest().getCreateTenantRequest();
+    final String tenantName = request.getTenantName();
+    final VolumeInfo volumeInfo =
+        getOmRequest().getCreateVolumeRequest().getVolumeInfo();
+    final String volumeName = volumeInfo.getVolume();
+    final String dbVolumeKey = omMetadataManager.getVolumeKey(volumeName);
+    IOException exception = null;
+    try {
+      // Check ACL: requires volume create permission. TODO: tenant create perm?
+      if (ozoneManager.getAclsEnabled()) {
+        checkAcls(ozoneManager, OzoneObj.ResourceType.VOLUME,
+            OzoneObj.StoreType.OZONE, IAccessAuthorizer.ACLType.CREATE,
+            tenantName, null, null);
+      }
+
+      acquiredVolumeLock = omMetadataManager.getLock().acquireWriteLock(
+          VOLUME_LOCK, volumeName);
+      // Check volume existence
+      if (omMetadataManager.getVolumeTable().isExist(volumeName)) {
+        LOG.debug("volume: {} already exists", volumeName);
+        throw new OMException("Volume already exists",
+            OMException.ResultCodes.VOLUME_ALREADY_EXISTS);
+      }
+      // Check tenant existence in tenantStateTable
+      if (omMetadataManager.getTenantStateTable().isExist(tenantName)) {
+        LOG.debug("tenant: {} already exists", tenantName);
+        throw new OMException("Tenant already exists",
+            OMException.ResultCodes.TENANT_ALREADY_EXISTS);
+      }
+
+      // Add to tenantStateTable. Redundant assignment for clarity
+      final String bucketNamespaceName = tenantName;
+      final String accountNamespaceName = tenantName;
+      final String userPolicyGroupName =
+          tenantName + OzoneConsts.DEFAULT_TENANT_USER_POLICY_SUFFIX;
+      final String bucketPolicyGroupName =
+          tenantName + OzoneConsts.DEFAULT_TENANT_BUCKET_POLICY_SUFFIX;
+      final OmDBTenantInfo omDBTenantInfo = new OmDBTenantInfo(
+          tenantName, bucketNamespaceName, accountNamespaceName,
+          userPolicyGroupName, bucketPolicyGroupName);
+      omMetadataManager.getTenantStateTable().addCacheEntry(
+          new CacheKey<>(tenantName),
+          new CacheValue<>(Optional.of(omDBTenantInfo), transactionLogIndex));
+
+      // Call OMMultiTenantManager
+//      tenant = ozoneManager.getMultiTenantManager().createTenant(tenantName);

Review comment:
       After a discussion, we think it should be better to move this chunk to `preExecute` in order to avoid writing to Ranger while holding a lock. But the (small) challenge then becomes how to properly clean up Ranger policy when the request fails at some point. CMIIW @avijayanhwx @prashantpogde 




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org


[GitHub] [ozone] prashantpogde commented on a change in pull request #2059: HDDS-4945. Initial prototype for MultiTenant support for Ozone.

Posted by GitBox <gi...@apache.org>.
prashantpogde commented on a change in pull request #2059:
URL: https://github.com/apache/ozone/pull/2059#discussion_r602007077



##########
File path: hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/MultiTenantGateKeeperRangerPlugin.java
##########
@@ -0,0 +1,358 @@
+package org.apache.hadoop.ozone.om.multitenant;
+
+import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ADMIN_CREATE_GROUP_HTTP_ENDPOINT;
+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_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.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.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_OM_CONNECTION_REQUEST_TIMEOUT;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_OM_CONNECTION_REQUEST_TIMEOUT_DEFAULT;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_OM_CONNECTION_TIMEOUT;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_OM_CONNECTION_TIMEOUT_DEFAULT;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_OM_IGNORE_SERVER_CERT;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_OM_IGNORE_SERVER_CERT_DEFAULT;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+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;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.commons.net.util.Base64;
+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.codehaus.jettison.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.oracle.javafx.jmx.json.JSONException;
+
+import javafx.util.Pair;
+
+public class MultiTenantGateKeeperRangerPlugin implements
+    MultiTenantGateKeeper {
+  private static final Logger LOG = LoggerFactory
+      .getLogger(MultiTenantGateKeeperRangerPlugin.class);
+
+  private static OzoneConfiguration conf;
+  private static boolean ignoreServerCert = false;
+  private static int connectionTimeout;
+  private static int connectionRequestTimeout;
+  private static String authHeaderValue;
+
+  @Override
+  public void init(Configuration configuration) throws IOException {
+    conf = (OzoneConfiguration)configuration;
+    initializeRangerConnection();
+  }
+
+  private void initializeRangerConnection() {
+    setupRangerConnectionConfig();
+    if (ignoreServerCert) {
+      setupRangerIgnoreServerCertificate();
+    }
+    setupRangerConnectionAuthHeader();
+  }
+
+  private void setupRangerConnectionConfig() {
+    connectionTimeout = (int) conf.getTimeDuration(
+        OZONE_RANGER_OM_CONNECTION_TIMEOUT,
+        conf.get(
+            OZONE_RANGER_OM_CONNECTION_TIMEOUT,
+            OZONE_RANGER_OM_CONNECTION_TIMEOUT_DEFAULT),
+        TimeUnit.MILLISECONDS);
+    connectionRequestTimeout = (int)conf.getTimeDuration(
+        OZONE_RANGER_OM_CONNECTION_REQUEST_TIMEOUT,
+        conf.get(
+            OZONE_RANGER_OM_CONNECTION_REQUEST_TIMEOUT,
+            OZONE_RANGER_OM_CONNECTION_REQUEST_TIMEOUT_DEFAULT),
+        TimeUnit.MILLISECONDS
+    );
+    ignoreServerCert = (boolean) conf.getBoolean(
+        OZONE_RANGER_OM_IGNORE_SERVER_CERT,
+            OZONE_RANGER_OM_IGNORE_SERVER_CERT_DEFAULT);
+  }
+
+  private void setupRangerIgnoreServerCertificate() {
+    // Create a trust manager that does not validate certificate chains
+    TrustManager[] trustAllCerts = new TrustManager[]{
+        new X509TrustManager() {
+          public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+            return null;
+          }
+          public void checkClientTrusted(
+              java.security.cert.X509Certificate[] certs, String authType) {
+          }
+          public void checkServerTrusted(
+              java.security.cert.X509Certificate[] certs, String authType) {
+          }
+        }
+    };
+
+    try {
+      SSLContext sc = SSLContext.getInstance("SSL");
+      sc.init(null, trustAllCerts, new java.security.SecureRandom());
+      HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
+    } catch (Exception e) {
+      LOG.info("Setting DefaultSSLSocketFactory failed.");
+    }
+  }
+
+  private void setupRangerConnectionAuthHeader() {
+    String userName = conf.get(OZONE_OM_RANGER_HTTPS_ADMIN_API_USER);
+    String passwd = conf.get(OZONE_OM_RANGER_HTTPS_ADMIN_API_PASSWD);
+    String auth = userName + ":" + passwd;
+    byte[] encodedAuth =
+        Base64.encodeBase64(auth.getBytes(StandardCharsets.UTF_8));
+    authHeaderValue = "Basic " + new String(encodedAuth);
+  }
+
+
+  @Override
+  public void shutdown() throws Exception {
+    // TBD
+  }
+
+  @Override
+  public void grantAccess(BucketNameSpace bucketNameSpace,
+                          OzoneMultiTenantPrincipal user, ACLType aclType) {
+    // TBD
+  }
+
+  @Override
+  public void revokeAccess(BucketNameSpace bucketNameSpace,
+                           OzoneMultiTenantPrincipal user, ACLType aclType) {
+    // TBD
+  }
+
+  @Override
+  public void grantAccess(AccountNameSpace accountNameSpace,
+                          OzoneMultiTenantPrincipal user, ACLType aclType) {
+    // TBD
+  }
+
+  @Override
+  public void revokeAccess(AccountNameSpace accountNameSpace,
+                           OzoneMultiTenantPrincipal user, ACLType aclType) {
+    // TBD
+  }
+
+  @Override
+  public List<Pair<BucketNameSpace, ACLType>>
+  getAllBucketNameSpaceAccessess(OzoneMultiTenantPrincipal user) {
+    // TBD
+    return null;
+  }
+
+  @Override
+  public boolean checkAccess(BucketNameSpace bucketNameSpace,
+                             OzoneMultiTenantPrincipal user) {
+    // TBD
+    return true;
+  }
+
+  @Override
+  public boolean checkAccess(AccountNameSpace accountNameSpace,
+                             OzoneMultiTenantPrincipal user) {
+    // TBD
+    return true;
+  }
+
+  @Override
+  public boolean checkAccess(IOzoneObj ozoneObject, RequestContext context)
+      throws OMException {
+    // 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;
+  }
+
+  public String createUser(String userName, List<String> groupIDs)
+      throws Exception {
+    String rangerHttpsAddress = conf.get(OZONE_RANGER_HTTPS_ADDRESS_KEY);
+    String rangerAdminUrl =
+        rangerHttpsAddress + OZONE_OM_RANGER_ADMIN_CREATE_USER_HTTP_ENDPOINT;
+
+    String jsonCreateUserString = getCreateUserJsonString(userName, groupIDs);
+
+    HttpsURLConnection conn = makeHttpsPostCall(rangerAdminUrl,
+        jsonCreateUserString,"POST", false);
+    String userInfo = getReponseData(conn);
+    String userIDCreated;
+    try {
+      JSONObject jObject = new JSONObject(userInfo.toString());
+      userIDCreated = jObject.getString("id");
+      System.out.println("User ID is : " + userIDCreated);
+    } catch (JSONException e) {
+      e.printStackTrace();
+      throw e;
+    }
+    return userIDCreated;
+  }
+
+  private String getCreateGroupJsonString(String groupName) throws Exception {
+    String jsonCreateGroupString = "{ \"name\":\"" + groupName + "\"," +
+        "  \"description\":\"test\" " +
+        " }";
+    return jsonCreateGroupString;
+  }
+
+  public String createGroup(String groupName) throws Exception {
+    String rangerHttpsAddress = conf.get(OZONE_RANGER_HTTPS_ADDRESS_KEY);

Review comment:
       done




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org


[GitHub] [ozone] prashantpogde commented on pull request #2059: HDDS-4945. Initial prototype for MultiTenant support for Ozone.

Posted by GitBox <gi...@apache.org>.
prashantpogde commented on pull request #2059:
URL: https://github.com/apache/ozone/pull/2059#issuecomment-823712040


   > Personally I think it's better to have a consensus before we start to implement code as part of HDDS jira issues.
   > 
   > This PR follows an approach which is under debate on the parent issue.
   > 
   > While any poc can be useful to show concepts it may be easier to separate these poc-s to the forks instead of creating PRs to avoid any confusions.
   > 
   > (the poc itself is on the fork, but I am not sure if this PR is useful in the current moment. Even if we have an agreement I assume it will be committed and reviewed in a smaller chunk, and we don't need to use CI minutes of apache organization to test as local forks also can execute all the tests)
   
   We are doing all this work in a separate branch. I will merge this from local fork to a branch. 
   We have shared this  work over multiple community meetings and various forums. We will continue discussions and address concerns.
   
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org


[GitHub] [ozone] prashantpogde commented on a change in pull request #2059: HDDS-4945. Initial prototype for MultiTenant support for Ozone.

Posted by GitBox <gi...@apache.org>.
prashantpogde commented on a change in pull request #2059:
URL: https://github.com/apache/ozone/pull/2059#discussion_r617139770



##########
File path: hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantUserCreateRequest.java
##########
@@ -0,0 +1,285 @@
+/*
+ * 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.codec.digest.DigestUtils;
+import org.apache.hadoop.hdds.utils.db.cache.CacheKey;
+import org.apache.hadoop.hdds.utils.db.cache.CacheValue;
+import org.apache.hadoop.ozone.OmUtils;
+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.S3SecretValue;
+import org.apache.hadoop.ozone.om.multitenant.OzoneMultiTenantPrincipal;
+import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerDoubleBufferHelper;
+import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
+import org.apache.hadoop.ozone.om.request.volume.OMVolumeRequest;
+import org.apache.hadoop.ozone.om.response.OMClientResponse;
+import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantCreateResponse;
+import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantUserCreateResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantUserRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantUserResponse;
+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.UpdateGetS3SecretRequest;
+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;
+
+/*
+  Ratis execution flow for OMTenantUserCreate
+
+- Client (UserCreateHandler , etc.)
+  - Check username validity: ensure no invalid characters
+  - Send request to server
+- OMTenantUserCreateRequest
+  - preExecute (perform checks and init)
+    - Check username validity (again), check $
+      - If username is invalid, throw exception to client; else continue
+    - Generate S3 secret for the new user
+  - validateAndUpdateCache (update DB)
+    - Permission check (checkACL need to check access key now)
+    - Grab VOLUME_LOCK write lock
+    - Check user existence
+      - If user doesn't exist, throw exception to client; else continue
+    - Check tenant existence
+      - If tenant doesn't exist, throw exception to client; else continue
+    - Grab S3_SECRET_LOCK write lock
+    - S3SecretTable: Flush generated S3 secret
+      - Key: TENANTNAME$USERNAME (equivalent to kerberosID)
+      - Value: <GENERATED_SECRET>
+    - Release S3_SECRET_LOCK write lock
+    - tenantUserTable: New entry
+      - Key: Tenant user name. e.g. finance$bob, s3v$alice
+      - Value: Tenant name. e.g. finance
+    - tenantGroupTable: Add this new user to the default tenant group.
+      - Key: finance$bob
+      - Value: finance-users
+    - tenantRoleTable: TBD. NoOp for prototype.
+    - Release VOLUME_LOCK write lock
+ */
+
+/**
+ * Handles OMTenantUserCreate request.
+ */
+public class OMTenantUserCreateRequest extends OMVolumeRequest {

Review comment:
       yup




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org


[GitHub] [ozone] adoroszlai edited a comment on pull request #2059: HDDS-4945. Initial prototype for MultiTenant support for Ozone.

Posted by GitBox <gi...@apache.org>.
adoroszlai edited a comment on pull request #2059:
URL: https://github.com/apache/ozone/pull/2059#issuecomment-819325489


   > we don't need to use CI minutes of apache organization to test as local forks also can execute all the tests)
   
   I'm planning to merge `master` into the feature branch to apply HDDS-4995 to it.  This would at least avoid the unnecessary builds.  (Waiting for [build results](https://github.com/adoroszlai/hadoop-ozone/actions/runs/747460165) in my fork.)


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org


[GitHub] [ozone] prashantpogde commented on a change in pull request #2059: HDDS-4945. Initial prototype for MultiTenant support for Ozone.

Posted by GitBox <gi...@apache.org>.
prashantpogde commented on a change in pull request #2059:
URL: https://github.com/apache/ozone/pull/2059#discussion_r617138747



##########
File path: hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManagerImpl.java
##########
@@ -0,0 +1,316 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.hadoop.ozone.om;
+
+import static org.apache.hadoop.ozone.om.multitenant.AccessPolicy.AccessGrantType.ALLOW;
+import static org.apache.hadoop.ozone.om.multitenant.OzoneMultiTenantPrincipal.OzonePrincipalType.GROUP_PRINCIPAL;
+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;
+import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.READ;
+import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.READ_ACL;
+import static org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.BUCKET;
+import static org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.KEY;
+import static org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.VOLUME;
+import static org.apache.hadoop.ozone.security.acl.OzoneObj.StoreType.OZONE;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.ozone.om.multitenant.AccessPolicy;
+import org.apache.hadoop.ozone.om.multitenant.AccountNameSpace;
+import org.apache.hadoop.ozone.om.multitenant.BucketNameSpace;
+import org.apache.hadoop.ozone.om.multitenant.CephCompatibleTenantImpl;
+import org.apache.hadoop.ozone.om.multitenant.MultiTenantGateKeeper;
+import org.apache.hadoop.ozone.om.multitenant.MultiTenantGateKeeperRangerPlugin;
+import org.apache.hadoop.ozone.om.multitenant.OzoneMultiTenantPrincipal;
+import org.apache.hadoop.ozone.om.multitenant.RangerAccessPolicy;
+import org.apache.hadoop.ozone.om.multitenant.Tenant;
+import org.apache.hadoop.ozone.om.multitenantImpl.OzoneMultiTenantPrincipalImpl;
+import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType;
+import org.apache.hadoop.ozone.security.acl.OzoneObj;
+import org.apache.hadoop.ozone.security.acl.OzoneObjInfo;
+import org.apache.http.auth.BasicUserPrincipal;
+
+public class OMMultiTenantManagerImpl implements OMMultiTenantManager {
+  private MultiTenantGateKeeper gateKeeper;
+  private OMMetadataManager omMetadataManager;
+  private OzoneConfiguration conf;
+  private Map<String, Tenant> allTenants;
+
+  OMMultiTenantManagerImpl(OMMetadataManager mgr, OzoneConfiguration conf)
+      throws IOException {
+    this.conf = conf;
+    allTenants = new ConcurrentHashMap<>();
+    omMetadataManager = mgr;
+    start(conf);
+  }
+
+  @Override
+  public void start(OzoneConfiguration configuration) throws IOException {
+    gateKeeper = new MultiTenantGateKeeperRangerPlugin();

Review comment:
       yup. This will change in the final productized version.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org


[GitHub] [ozone] smengcl commented on a change in pull request #2059: HDDS-4945. Initial prototype for MultiTenant support for Ozone.

Posted by GitBox <gi...@apache.org>.
smengcl commented on a change in pull request #2059:
URL: https://github.com/apache/ozone/pull/2059#discussion_r605931403



##########
File path: hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantCreateRequest.java
##########
@@ -0,0 +1,300 @@
+/*
+ * 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.OmUtils;
+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.OmDBTenantInfo;
+import org.apache.hadoop.ozone.om.helpers.OmVolumeArgs;
+import org.apache.hadoop.ozone.om.multitenant.Tenant;
+import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerDoubleBufferHelper;
+import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
+import org.apache.hadoop.ozone.om.request.volume.OMVolumeRequest;
+import org.apache.hadoop.ozone.om.response.OMClientResponse;
+import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantCreateResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateVolumeRequest;
+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.VolumeInfo;
+import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer;
+import org.apache.hadoop.ozone.security.acl.OzoneObj;
+import org.apache.hadoop.ozone.storage.proto.OzoneManagerStorageProtos.PersistedUserVolumeInfo;
+import org.apache.hadoop.util.Time;
+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.USER_LOCK;
+import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_LOCK;
+
+/*
+  Ratis execution flow for OMTenantCreate
+
+- preExecute (perform checks and init)
+  - Check tenant name validity (again)
+    - If name is invalid, throw exception to client; else continue
+- validateAndUpdateCache (update DB)
+  - Grab VOLUME_LOCK write lock
+  - Check volume existence
+    - If tenant already exists, throw exception to client; else continue
+  - Check tenant existence by checking tenantStateTable keys
+    - If tenant already exists, throw exception to client; else continue
+  - tenantStateTable: New entry
+    - Key: tenant name. e.g. finance
+    - Value: new OmDBTenantInfo for the tenant
+      - tenantName: finance
+      - bucketNamespaceName: finance
+      - accountNamespaceName: finance
+      - userPolicyGroupName: finance-users
+      - bucketPolicyGroupName: finance-buckets
+  - tenantPolicyTable: Generate default policies for the new tenant
+    - K: finance-Users, V: finance-users-default
+    - K: finance-Buckets, V: finance-buckets-default
+  - Grab USER_LOCK write lock
+  - Create volume finance (See OMVolumeCreateRequest)
+  - Release VOLUME_LOCK write lock
+  - Release USER_LOCK write lock
+  - Queue Ranger policy sync that pushes default policies:
+      OMMultiTenantManager#createTenant
+ */
+
+/**
+ * Handles OMTenantCreate request.
+ */
+public class OMTenantCreateRequest extends OMVolumeRequest {
+  private static final Logger LOG =
+      LoggerFactory.getLogger(OMTenantCreateRequest.class);
+
+  public OMTenantCreateRequest(OMRequest omRequest) {
+    super(omRequest);
+  }
+
+  @Override
+  public OMRequest preExecute(OzoneManager ozoneManager) throws IOException {
+    final CreateTenantRequest request = getOmRequest().getCreateTenantRequest();
+    final String tenantName = request.getTenantName();
+
+    // Check tenantName validity
+    if (tenantName.contains(OzoneConsts.TENANT_NAME_USER_NAME_DELIMITER)) {
+      throw new OMException("Invalid tenant name " + tenantName +
+          ". Tenant name should not contain delimiter.",
+          OMException.ResultCodes.INVALID_VOLUME_NAME);
+    }
+
+    // getUserName returns:
+    // - Kerberos principal when Kerberos security is enabled
+    // - User's login name when security is not enabled
+    // - AWS_ACCESS_KEY_ID if the original request comes from S3 Gateway.
+    //    Not Applicable to TenantCreateRequest.
+    final String owner = getOmRequest().getUserInfo().getUserName();
+    final String volumeName = tenantName;  // TODO: Configurable
+    final VolumeInfo volumeInfo = VolumeInfo.newBuilder()
+        .setVolume(volumeName)
+        .setAdminName(owner)
+        .setOwnerName(owner)
+        .build();
+    // Verify volume name
+    OmUtils.validateVolumeName(volumeInfo.getVolume());
+
+    // Generate volume modification time
+    long initialTime = Time.now();
+    final VolumeInfo updatedVolumeInfo = volumeInfo.toBuilder()
+            .setCreationTime(initialTime)
+            .setModificationTime(initialTime)
+            .build();
+
+    final OMRequest.Builder omRequestBuilder = getOmRequest().toBuilder()
+        .setCreateTenantRequest(
+            CreateTenantRequest.newBuilder().setTenantName(tenantName))
+        .setCreateVolumeRequest(
+            CreateVolumeRequest.newBuilder().setVolumeInfo(updatedVolumeInfo))
+        .setUserInfo(getUserInfo())
+        .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;
+    OMResponse.Builder omResponse = OmResponseUtil.getOMResponseBuilder(
+        getOmRequest());
+    OmVolumeArgs omVolumeArgs;
+    boolean acquiredVolumeLock = false, acquiredUserLock = false;
+    Tenant tenant = null;
+    final String owner = getOmRequest().getUserInfo().getUserName();
+    LOG.info("owner: {}", owner);  // TODO: owner should be access_key_id
+    Map<String, String> auditMap = new HashMap<>();
+    OMMetadataManager omMetadataManager = ozoneManager.getMetadataManager();
+    CreateTenantRequest request = getOmRequest().getCreateTenantRequest();
+    final String tenantName = request.getTenantName();
+    final VolumeInfo volumeInfo =
+        getOmRequest().getCreateVolumeRequest().getVolumeInfo();
+    final String volumeName = volumeInfo.getVolume();
+    final String dbVolumeKey = omMetadataManager.getVolumeKey(volumeName);
+    IOException exception = null;
+    try {
+      // Check ACL: requires volume create permission. TODO: tenant create perm?

Review comment:
       With the current logic, yes.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org


[GitHub] [ozone] smengcl commented on a change in pull request #2059: HDDS-4945. Initial prototype for MultiTenant support for Ozone.

Posted by GitBox <gi...@apache.org>.
smengcl commented on a change in pull request #2059:
URL: https://github.com/apache/ozone/pull/2059#discussion_r605932511



##########
File path: hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantUserCreateRequest.java
##########
@@ -0,0 +1,285 @@
+/*
+ * 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.codec.digest.DigestUtils;
+import org.apache.hadoop.hdds.utils.db.cache.CacheKey;
+import org.apache.hadoop.hdds.utils.db.cache.CacheValue;
+import org.apache.hadoop.ozone.OmUtils;
+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.S3SecretValue;
+import org.apache.hadoop.ozone.om.multitenant.OzoneMultiTenantPrincipal;
+import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerDoubleBufferHelper;
+import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
+import org.apache.hadoop.ozone.om.request.volume.OMVolumeRequest;
+import org.apache.hadoop.ozone.om.response.OMClientResponse;
+import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantCreateResponse;
+import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantUserCreateResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantUserRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantUserResponse;
+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.UpdateGetS3SecretRequest;
+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;
+
+/*
+  Ratis execution flow for OMTenantUserCreate
+
+- Client (UserCreateHandler , etc.)
+  - Check username validity: ensure no invalid characters
+  - Send request to server
+- OMTenantUserCreateRequest
+  - preExecute (perform checks and init)
+    - Check username validity (again), check $
+      - If username is invalid, throw exception to client; else continue
+    - Generate S3 secret for the new user
+  - validateAndUpdateCache (update DB)
+    - Permission check (checkACL need to check access key now)
+    - Grab VOLUME_LOCK write lock
+    - Check user existence
+      - If user doesn't exist, throw exception to client; else continue
+    - Check tenant existence
+      - If tenant doesn't exist, throw exception to client; else continue
+    - Grab S3_SECRET_LOCK write lock
+    - S3SecretTable: Flush generated S3 secret
+      - Key: TENANTNAME$USERNAME (equivalent to kerberosID)
+      - Value: <GENERATED_SECRET>
+    - Release S3_SECRET_LOCK write lock
+    - tenantUserTable: New entry
+      - Key: Tenant user name. e.g. finance$bob, s3v$alice
+      - Value: Tenant name. e.g. finance
+    - tenantGroupTable: Add this new user to the default tenant group.
+      - Key: finance$bob
+      - Value: finance-users
+    - tenantRoleTable: TBD. NoOp for prototype.
+    - Release VOLUME_LOCK write lock
+ */
+
+/**
+ * Handles OMTenantUserCreate request.
+ */
+public class OMTenantUserCreateRequest extends OMVolumeRequest {

Review comment:
       Yes we'd better have one as of right now the only way is retrieve the creds after tenant user creation is to dump the DB.. maybe allow it for admin at least




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org


[GitHub] [ozone] prashantpogde commented on a change in pull request #2059: HDDS-4945. Initial prototype for MultiTenant support for Ozone.

Posted by GitBox <gi...@apache.org>.
prashantpogde commented on a change in pull request #2059:
URL: https://github.com/apache/ozone/pull/2059#discussion_r602006764



##########
File path: hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/AccountNameSpace.java
##########
@@ -0,0 +1,21 @@
+package org.apache.hadoop.ozone.om.multitenant;
+
+
+import org.apache.hadoop.hdds.annotation.InterfaceAudience;
+import org.apache.hadoop.hdds.annotation.InterfaceStability;
+
+@InterfaceAudience.LimitedPrivate({"HDFS", "Yarn", "Ranger", "Hive", "HBase"})
+@InterfaceStability.Evolving
+
+public interface AccountNameSpace {

Review comment:
       done

##########
File path: hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/BucketNameSpace.java
##########
@@ -0,0 +1,37 @@
+package org.apache.hadoop.ozone.om.multitenant;
+
+import java.util.List;
+
+import org.apache.hadoop.hdds.annotation.InterfaceAudience;
+import org.apache.hadoop.hdds.annotation.InterfaceStability;
+import org.apache.hadoop.ozone.security.acl.OzoneObj;
+
+@InterfaceAudience.LimitedPrivate({"HDFS", "Yarn", "Ranger", "Hive", "HBase"})
+@InterfaceStability.Evolving
+public interface BucketNameSpace {

Review comment:
       done

##########
File path: hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/AccessPolicy.java
##########
@@ -0,0 +1,32 @@
+/**
+ * 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.multitenant;
+
+import java.security.Principal;
+
+import org.apache.hadoop.hdds.annotation.InterfaceAudience;
+import org.apache.hadoop.hdds.annotation.InterfaceStability;
+
+@InterfaceAudience.LimitedPrivate({"HDFS", "Yarn", "Ranger", "Hive", "HBase"})
+@InterfaceStability.Evolving
+public interface AccessPolicy {
+
+  void createPolicy(String policyJsonString);

Review comment:
       Done

##########
File path: hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManager.java
##########
@@ -0,0 +1,237 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership.  The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.hadoop.ozone.om;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.ozone.om.multitenant.AccessPolicy;
+import org.apache.hadoop.ozone.om.multitenant.AccountNameSpace;
+import org.apache.hadoop.ozone.om.multitenant.BucketNameSpace;
+import org.apache.hadoop.ozone.om.multitenant.OzoneMultiTenantPrincipal;
+import org.apache.hadoop.ozone.om.multitenant.Tenant;
+
+import javafx.util.Pair;
+
+/**
+ * OM MultiTenant manager interface.
+ */
+public interface OMMultiTenantManager {
+  /**
+   * Start multi-tenant manager. Performs initialization e.g.
+   *  - Initialize Multi-Tenant-Gatekeeper-Plugin
+   *  - Validate Multi-Tenant Bucket-NameSpaces
+   *  - Validate Multi-Tenant Account-NameSpaces
+   *  - Validating various OM (Multi-Tenant state)tables and corresponding
+   *    state in IMultiTenantGateKeeperPlugin (Ranger/Native/AnyOtherPlugIn).
+   *  - Setup SuperUsers for Multi-Tenant environment from Ozone-Conf
+   *  - Periodic BackGround thread to keep MultiTenant-State consistent e.g.
+   *       . superusers  <-in-sync-> OzoneConf,
+   *       . OM-DB state <-in-sync-> IMultiTenantGateKeeperPluginState
+   *       . OM DB state is always the source of truth.
+   *
+   * @param configuration
+   * @throws IOException
+   */
+  void start(OzoneConfiguration configuration) throws IOException;
+
+  /**
+   * Stop multi-tenant manager.
+   */
+  void stop() throws Exception;
+
+  /**
+   * Returns the corresponding OzoneManager instance.
+   *
+   * @return OMMetadataManager
+   */
+  OMMetadataManager getOzoneManager();
+
+  /**
+   * Given a TenantID String, Create and return Tenant Interface.
+   *
+   * @param tenantID
+   * @return Tenant interface.
+   */
+  Tenant createTenant(String tenantID) throws IOException;

Review comment:
       done.

##########
File path: hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManager.java
##########
@@ -0,0 +1,237 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership.  The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.hadoop.ozone.om;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.ozone.om.multitenant.AccessPolicy;
+import org.apache.hadoop.ozone.om.multitenant.AccountNameSpace;
+import org.apache.hadoop.ozone.om.multitenant.BucketNameSpace;
+import org.apache.hadoop.ozone.om.multitenant.OzoneMultiTenantPrincipal;
+import org.apache.hadoop.ozone.om.multitenant.Tenant;
+
+import javafx.util.Pair;
+
+/**
+ * OM MultiTenant manager interface.
+ */
+public interface OMMultiTenantManager {
+  /**
+   * Start multi-tenant manager. Performs initialization e.g.
+   *  - Initialize Multi-Tenant-Gatekeeper-Plugin
+   *  - Validate Multi-Tenant Bucket-NameSpaces
+   *  - Validate Multi-Tenant Account-NameSpaces
+   *  - Validating various OM (Multi-Tenant state)tables and corresponding
+   *    state in IMultiTenantGateKeeperPlugin (Ranger/Native/AnyOtherPlugIn).
+   *  - Setup SuperUsers for Multi-Tenant environment from Ozone-Conf
+   *  - Periodic BackGround thread to keep MultiTenant-State consistent e.g.
+   *       . superusers  <-in-sync-> OzoneConf,
+   *       . OM-DB state <-in-sync-> IMultiTenantGateKeeperPluginState
+   *       . OM DB state is always the source of truth.
+   *
+   * @param configuration
+   * @throws IOException
+   */
+  void start(OzoneConfiguration configuration) throws IOException;
+
+  /**
+   * Stop multi-tenant manager.
+   */
+  void stop() throws Exception;
+
+  /**
+   * Returns the corresponding OzoneManager instance.
+   *
+   * @return OMMetadataManager
+   */
+  OMMetadataManager getOzoneManager();

Review comment:
       done




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org


[GitHub] [ozone] prashantpogde commented on a change in pull request #2059: HDDS-4945. Initial prototype for MultiTenant support for Ozone.

Posted by GitBox <gi...@apache.org>.
prashantpogde commented on a change in pull request #2059:
URL: https://github.com/apache/ozone/pull/2059#discussion_r617139568



##########
File path: hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/RangerAccessPolicy.java
##########
@@ -0,0 +1,250 @@
+/*
+ * 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.multitenant;
+
+import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType;
+import org.apache.hadoop.ozone.security.acl.OzoneObj;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.apache.hadoop.ozone.om.multitenant.AccessPolicy.AccessPolicyType.RANGER_POLICY;
+import static org.apache.hadoop.ozone.om.multitenant.OzoneMultiTenantPrincipal.OzonePrincipalType.GROUP_PRINCIPAL;
+
+public class RangerAccessPolicy implements AccessPolicy {
+
+  // For now RangerAccessPolicy supports only one object per policy
+  private OzoneObj accessObject;
+  private Map<String, List<AccessPolicyElem>> policyMap;
+  private String policyID;
+  private String policyJsonString;
+  private String policyName;
+
+  public RangerAccessPolicy(String name) {
+    policyMap = new ConcurrentHashMap<>();
+    policyName = name;
+  }
+
+  public void setPolicyID(String id) {
+    policyID = id;
+  }
+
+  public String getPolicyID() {
+    return policyID;
+  }
+
+  public String getPolicyName() {
+    return policyName;
+  }
+
+  @Override
+  public String getPolicyJsonString() throws Exception {
+    updatePolicyJsonString();
+    return policyJsonString;
+  }
+
+  @Override
+  public AccessPolicyType getAccessPolicyType() {
+    return RANGER_POLICY;
+  }
+
+  @Override
+  public void addAccessPolicyElem(OzoneObj object,
+                                  OzoneMultiTenantPrincipal principal,
+                                  ACLType acl, AccessGrantType grant)
+      throws IOException {
+    if (accessObject == null) {
+      accessObject = object;
+    } else if (!object.toString().equals(accessObject.toString())) {
+      throw new IOException(
+          "RangerAccessPolicy supports only one object per" + " policy");
+    }
+    AccessPolicyElem elem = new AccessPolicyElem(object, principal, acl, grant);
+    if (!policyMap.containsKey(principal.toString())) {
+      List<AccessPolicyElem> elemList = new ArrayList<>();
+      elemList.add(elem);
+      policyMap.put(principal.toString(), elemList);
+      return;
+    }
+    List<AccessPolicyElem> elemList = policyMap.get(principal.toString());
+    for (AccessPolicyElem e : elemList) {
+      if (e.getAclType() == acl) {
+        throw new IOException(
+            "RangerAccessPolicy: Principal " + principal.toString()
+                + " already exists with access " + acl);
+      }
+    }
+    elemList.add(elem);
+  }
+
+  @Override
+  public List<AccessPolicyElem> getAccessPolicyElem() {
+    List<AccessPolicyElem> list = new ArrayList<>();
+    for (Map.Entry<String, List<AccessPolicyElem>> entry : policyMap
+        .entrySet()) {
+      list.addAll(entry.getValue());
+    }
+    return list;
+  }
+
+  @Override public void removeAccessPolicyElem(OzoneObj object,
+      OzoneMultiTenantPrincipal principal, ACLType acl, AccessGrantType grant)
+      throws IOException {
+    if (accessObject == null) {
+      throw new IOException("removeAccessPolicyElem: Invalid Arguments.");
+    } else if (!object.toString().equals(accessObject.toString())) {
+      throw new IOException(
+          "removeAccessPolicyElem:  Object not found." + object.toString());
+    }
+    if (!policyMap.containsKey(principal.toString())) {
+      throw new IOException(
+          "removeAccessPolicyElem:  Principal not found." + object.toString());
+    }
+    List<AccessPolicyElem> elemList = policyMap.get(principal.toString());
+    for (AccessPolicyElem e : elemList) {
+      if (e.getAclType() == acl) {
+        elemList.remove(e);
+      }
+    }
+    if (elemList.isEmpty()) {
+      policyMap.remove(principal.toString());
+    }
+    throw new IOException(
+        "removeAccessPolicyElem:  aclType not found." + object.toString());
+  }
+
+  private String createRangerResourceItems() throws IOException {
+    StringBuilder resourceItems = new StringBuilder();
+    resourceItems.append("\"resources\":{" +
+        "\"volume\":{" +
+        "\"values\":[\"");
+    resourceItems.append(accessObject.getVolumeName());
+    resourceItems.append("\"]," +
+        "\"isRecursive\":false," +
+        "\"isExcludes\":false" +
+        "}");
+    if ((accessObject.getResourceType() == OzoneObj.ResourceType.BUCKET) ||
+        (accessObject.getResourceType() == OzoneObj.ResourceType.KEY)) {
+      resourceItems.append(
+          ",\"bucket\":{" +
+          "\"values\":[\"");
+      resourceItems.append(accessObject.getBucketName());
+      resourceItems.append("\"]," +
+          "\"isRecursive\":false," +
+          "\"isExcludes\":false" +
+          "}");
+    }
+    if (accessObject.getResourceType() == OzoneObj.ResourceType.KEY) {
+      resourceItems.append(",\"key\":{" +
+          "\"values\":[\"");
+      resourceItems.append(accessObject.getKeyName());
+      resourceItems.append("\"]," +
+          "\"isRecursive\":true," +
+          "\"isExcludes\":false" +
+          "}");
+    }
+    resourceItems.append("},");
+    return resourceItems.toString();
+  }
+
+  private String createRangerPolicyItems() throws IOException {
+    StringBuilder policyItems = new StringBuilder();
+    policyItems.append("\"policyItems\":[");
+    int mapRemainingSize = policyMap.size();
+    for (Map.Entry<String, List<AccessPolicyElem>> mapElem : policyMap
+        .entrySet()) {
+      mapRemainingSize--;
+      List<AccessPolicyElem> list = mapElem.getValue();
+      if (list.isEmpty()) {
+        continue;
+      }
+      policyItems.append("{");
+      if (list.get(0).getPrincipal().getUserPrincipalType()
+          == GROUP_PRINCIPAL) {
+        policyItems.append("\"groups\":[\"" + mapElem.getKey() + "\"],");
+      } else {
+        policyItems.append("\"users\":[\"" + mapElem.getKey() + "\"],");
+      }
+      policyItems.append("\"accesses\":[");
+      Iterator<AccessPolicyElem> iter = list.iterator();
+      while (iter.hasNext()) {
+        AccessPolicyElem elem = iter.next();
+        policyItems.append("{");
+        policyItems.append("\"type\":\"");
+        policyItems.append(getRangerAclString(elem.getAclType()));
+        policyItems.append("\",");
+        if (elem.getAccessGrantType() == AccessGrantType.ALLOW) {
+          policyItems.append("\"isAllowed\":true");
+        } else {
+          policyItems.append("\"isDenied\":true");
+        }
+        policyItems.append("}");
+        if (iter.hasNext()) {
+          policyItems.append(",");
+        }
+      }
+      policyItems.append("]");
+      policyItems.append("}");
+      if (mapRemainingSize > 0) {
+        policyItems.append(",");
+      }
+    }
+    policyItems.append("],");
+    return policyItems.toString();
+  }
+
+  private String getRangerAclString(ACLType aclType) throws IOException {
+    switch (aclType) {
+    case ALL:
+      return "All";
+    case LIST:
+      return "List";
+    case READ:
+      return "Read";
+    case WRITE:
+      return "Write";
+    case CREATE:
+      return "Create";
+    case DELETE:
+      return "Delete";
+    case READ_ACL:
+      return "Read_ACL";
+    case WRITE_ACL:
+      return "Write_ACL";
+    case NONE:
+      return "";
+    default:
+      throw new IOException("Unknown ACLType");
+    }
+  }
+
+  private void updatePolicyJsonString() throws Exception {
+    policyJsonString =
+        "{\"policyType\":\"0\"," + "\"name\":\"" + policyName + "\","
+            + "\"isEnabled\":true," + "\"policyPriority\":0,"
+            + "\"policyLabels\":[]," + "\"description\":\"\","
+            + "\"isAuditEnabled\":true," + createRangerResourceItems()
+            + "\"isDenyAllElse\":false," + createRangerPolicyItems()
+            + "\"allowExceptions\":[]," + "\"denyPolicyItems\":[],"
+            + "\"denyExceptions\":[]," + "\"service\":\"cm_ozone\"" + "}";

Review comment:
       Yes.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org


[GitHub] [ozone] adoroszlai commented on pull request #2059: HDDS-4945. Initial prototype for MultiTenant support for Ozone.

Posted by GitBox <gi...@apache.org>.
adoroszlai commented on pull request #2059:
URL: https://github.com/apache/ozone/pull/2059#issuecomment-819325489


   > we don't need to use CI minutes of apache organization to test as local forks also can execute all the tests)
   
   I'm planning to merge `master` into the feature branch to apply HDDS-4995 to it.  This would at least avoid the unnecessary builds.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org


[GitHub] [ozone] prashantpogde commented on a change in pull request #2059: HDDS-4945. Initial prototype for MultiTenant support for Ozone.

Posted by GitBox <gi...@apache.org>.
prashantpogde commented on a change in pull request #2059:
URL: https://github.com/apache/ozone/pull/2059#discussion_r617139427



##########
File path: hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantGateKeeperRangerPlugin.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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.hadoop.ozone.om.multitenant;
+
+import 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.apache.hadoop.ozone.om.multitenant.AccessPolicy.AccessGrantType.ALLOW;
+import static org.apache.hadoop.ozone.om.multitenant.OzoneMultiTenantPrincipal.OzonePrincipalType.GROUP_PRINCIPAL;
+import static org.apache.hadoop.ozone.om.multitenant.OzoneMultiTenantPrincipal.OzonePrincipalType.USER_PRINCIPAL;
+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;
+import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.READ;
+import static org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.READ_ACL;
+import static org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.BUCKET;
+import static org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.KEY;
+import static org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.VOLUME;
+import static org.apache.hadoop.ozone.security.acl.OzoneObj.StoreType.OZONE;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.ozone.om.multitenantImpl.OzoneMultiTenantPrincipalImpl;
+import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType;
+import org.apache.hadoop.ozone.security.acl.OzoneObjInfo;
+import org.apache.http.auth.BasicUserPrincipal;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.Timeout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Tests TestMultiTenantGateKeeperImplWithRanger.
+ * Marking it as Ignore because it needs Ranger access point.
+ */
+@Ignore
+public class TestMultiTenantGateKeeperRangerPlugin {
+  private static final Logger LOG = LoggerFactory
+      .getLogger(TestMultiTenantGateKeeperRangerPlugin.class);
+
+  /**
+   * Set a timeout for each test.
+   */
+  @Rule
+  public Timeout timeout = new Timeout(300000);
+
+  // The following values need to be set before this test can be enabled.
+  private static final String RANGER_ENDPOINT = "";
+  private static final String RANGER_ENDPOINT_USER = "";
+  private static final String RANGER_ENDPOINT_USER_PASSWD = "";
+
+  private List<String> usersIdsCreated = new ArrayList<String>();
+  private List<String> groupIdsCreated = new ArrayList<String>();
+  private List<String> policyIdsCreated = new ArrayList<String>();
+
+  private static OzoneConfiguration conf;
+
+  @BeforeClass
+  public static void init() throws Exception {
+    conf = new OzoneConfiguration();
+    simulateOzoneSiteXmlConfig();
+  }
+
+  @AfterClass
+  public static void shutdown() {
+  }
+
+  private static void simulateOzoneSiteXmlConfig() {
+    conf.setStrings(OZONE_RANGER_HTTPS_ADDRESS_KEY, RANGER_ENDPOINT);
+    conf.setStrings(OZONE_OM_RANGER_HTTPS_ADMIN_API_USER, RANGER_ENDPOINT_USER);
+    conf.setStrings(OZONE_OM_RANGER_HTTPS_ADMIN_API_PASSWD,
+        RANGER_ENDPOINT_USER_PASSWD);
+  }
+
+  @Test
+  public void testMultiTenantGateKeeperRangerPlugin() throws Exception {
+    simulateOzoneSiteXmlConfig();
+    MultiTenantGateKeeper omm = new MultiTenantGateKeeperRangerPlugin();
+    omm.init(conf);
+
+    try {
+      OzoneMultiTenantPrincipal group1Principal = getTestPrincipal("tenant1",
+          "groupTestAdmin", GROUP_PRINCIPAL);
+      OzoneMultiTenantPrincipal group2Principal = getTestPrincipal("tenant1",
+          "groupTestUsers", GROUP_PRINCIPAL);
+      groupIdsCreated.add(omm.createGroup(group1Principal));
+      groupIdsCreated.add(omm.createGroup(group2Principal));
+
+      OzoneMultiTenantPrincipal userPrincipal =
+          getTestPrincipal("tenant1", "user1Test", USER_PRINCIPAL);
+      usersIdsCreated.add(omm.createUser(userPrincipal, groupIdsCreated));
+
+      AccessPolicy tenant1VolumeAccessPolicy = createVolumeAccessPolicy(
+          "vol1", "tenant1", "Users");
+      policyIdsCreated.add(omm.createAccessPolicy(tenant1VolumeAccessPolicy));
+
+      AccessPolicy tenant1BucketCreatePolicy = allowCreateBucketPolicy(
+          "vol1", "tenant1", "Users");
+      policyIdsCreated.add(omm.createAccessPolicy(tenant1BucketCreatePolicy));
+
+      AccessPolicy tenant1BucketAccessPolicy = allowAccessBucketPolicy(
+          "vol1", "tenant1", "Users", "bucket1");
+      policyIdsCreated.add(omm.createAccessPolicy(tenant1BucketAccessPolicy));
+
+      AccessPolicy tenant1KeyAccessPolicy = allowAccessKeyPolicy(
+          "vol1", "tenant1", "Users", "bucket1");
+      policyIdsCreated.add(omm.createAccessPolicy(tenant1KeyAccessPolicy));
+
+    } catch (Exception e) {
+      Assert.fail(e.getMessage());
+    } finally {
+      for (String id : policyIdsCreated) {
+        omm.deletePolicy(id);
+      }
+      if (usersIdsCreated.size() == 1) {
+        omm.deleteUser(usersIdsCreated.get(0));
+      }
+      for (String id : groupIdsCreated) {
+        omm.deleteGroup(id);
+      }
+    }
+  }
+
+  OzoneMultiTenantPrincipal getTestPrincipal(String tenant, String id,
+      OzoneMultiTenantPrincipal.OzonePrincipalType type) {
+    OzoneMultiTenantPrincipal principal = new OzoneMultiTenantPrincipalImpl(
+        new BasicUserPrincipal(tenant),
+        new BasicUserPrincipal(id), type);
+    return principal;
+  }
+
+  AccessPolicy createVolumeAccessPolicy(String vol, String tenant,

Review comment:
       yup, We will refactor.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org


[GitHub] [ozone] elek edited a comment on pull request #2059: HDDS-4945. Initial prototype for MultiTenant support for Ozone.

Posted by GitBox <gi...@apache.org>.
elek edited a comment on pull request #2059:
URL: https://github.com/apache/ozone/pull/2059#issuecomment-802103225


   Thanks to create this patch @prashantpogde. I always love to talk about proposals based on example code and proof of concepts.
   
   One missing point for me is the design doc and public discussion about the design. As far as I see it's not uploaded to the jira.
   
   While I am very grateful to have this which can make easier to understand the proposed design, I think we need to keep it in Draft state until the end of the design discussion.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org


[GitHub] [ozone] avijayanhwx commented on a change in pull request #2059: HDDS-4945. Initial prototype for MultiTenant support for Ozone.

Posted by GitBox <gi...@apache.org>.
avijayanhwx commented on a change in pull request #2059:
URL: https://github.com/apache/ozone/pull/2059#discussion_r598777678



##########
File path: hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantGateKeeperRangerPlugin.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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.hadoop.ozone.om.multitenant;
+
+import static org.apache.hadoop.ozone.OzoneConsts.OZONE_OM_RANGER_ADMIN_CREATE_GROUP_HTTP_ENDPOINT;
+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_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.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.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_OM_CONNECTION_REQUEST_TIMEOUT;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_OM_CONNECTION_REQUEST_TIMEOUT_DEFAULT;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_OM_CONNECTION_TIMEOUT;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_OM_CONNECTION_TIMEOUT_DEFAULT;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.apache.commons.net.util.Base64;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.security.authentication.client.AuthenticationException;
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.Timeout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Tests TestMultiTenantGateKeeperImplWithRanger.
+ * Marking it as Ignore because it needs Ranger access point.
+ */
+public class TestMultiTenantGateKeeperRangerPlugin {
+  private static final Logger LOG = LoggerFactory
+      .getLogger(TestMultiTenantGateKeeperRangerPlugin.class);
+
+  /**
+   * Set a timeout for each test.
+   */
+  @Rule
+  public Timeout timeout = new Timeout(300000);
+
+  // The following values need to be set before this test can be enabled.
+  private static final String RANGER_ENDPOINT =
+      "https://s3-tenant-1.s3-tenant.root.hwx.site:6182";

Review comment:
       Can we remove this internal host detail?




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org


[GitHub] [ozone] smengcl commented on pull request #2059: HDDS-4945. Initial prototype for MultiTenant support for Ozone.

Posted by GitBox <gi...@apache.org>.
smengcl commented on pull request #2059:
URL: https://github.com/apache/ozone/pull/2059#issuecomment-877469394


   Closing this PR as I pull in all the master branch changes to branch HDDS-4944.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org