This is an automated email from the ASF dual-hosted git repository.
madhan pushed a commit to branch ranger-2.8
in repository https://gitbox.apache.org/repos/asf/ranger.git
The following commit(s) were added to refs/heads/ranger-2.8 by this push:
new 84bcfcf5a RANGER-5393: updated RangerOzoneAuthorizer to support
AssumeRole (#766)
84bcfcf5a is described below
commit 84bcfcf5a322aa97bf8b7264357cfeb377072e6c
Author: Madhan Neethiraj <[email protected]>
AuthorDate: Mon Dec 15 16:25:55 2025 -0800
RANGER-5393: updated RangerOzoneAuthorizer to support AssumeRole (#766)
(cherry picked from commit 3c8e5323c0d8a8cb4ece3be486f311f2535dfe1e)
---
.../service-defs/ranger-servicedef-ozone.json | 27 ++-
plugin-ozone/pom.xml | 18 +-
.../ozone/authorizer/RangerOzoneAuthorizer.java | 187 ++++++++++++++++++--
.../authorizer/TestRangerOzoneAuthorizer.java | 196 +++++++++++++++++++++
plugin-ozone/src/test/resources/om_dev_ozone.json | 46 +++++
.../src/test/resources/ranger-ozone-security.xml | 29 +++
pom.xml | 7 +-
.../ozone/authorizer/RangerOzoneAuthorizer.java | 16 ++
8 files changed, 506 insertions(+), 20 deletions(-)
diff --git
a/agents-common/src/main/resources/service-defs/ranger-servicedef-ozone.json
b/agents-common/src/main/resources/service-defs/ranger-servicedef-ozone.json
index 2bff90d47..025e5fa08 100755
--- a/agents-common/src/main/resources/service-defs/ranger-servicedef-ozone.json
+++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-ozone.json
@@ -25,6 +25,7 @@
"uiHint":"",
"label": "Ozone Volume",
"description": "Ozone Volume",
+ "accessTypeRestrictions": [ "read", "write", "create",
"list", "delete", "read_acl", "write_acl", "all" ],
"isValidLeaf": true
},
{
@@ -44,9 +45,9 @@
"uiHint":"",
"label": "Ozone Bucket",
"description": "Ozone Bucket",
+ "accessTypeRestrictions": [ "read", "write", "create",
"list", "delete", "read_acl", "write_acl", "all" ],
"isValidLeaf": true
},
-
{
"itemId": 3,
"name": "key",
@@ -64,7 +65,25 @@
"uiHint":"",
"label": "Ozone Key",
"description": "Ozone Key",
+ "accessTypeRestrictions": [ "read", "write", "create",
"list", "delete", "read_acl", "write_acl", "all" ],
"isValidLeaf": true
+ },
+ {
+ "itemId": 4,
+ "name": "role",
+ "type": "string",
+ "level": 10,
+ "parent": "",
+ "mandatory": true,
+ "lookupSupported": true,
+ "recursiveSupported": false,
+ "excludesSupported": false,
+ "matcher":
"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher",
+ "matcherOptions": { "wildCard":true,
"ignoreCase":false },
+ "label": "Role",
+ "description": "Role",
+ "accessTypeRestrictions": [ "assume_role" ],
+ "isValidLeaf": true
}
],
@@ -130,6 +149,12 @@
"name": "write_acl",
"label": "Write_ACL",
"category": "UPDATE"
+ },
+ {
+ "itemId": 8,
+ "name": "assume_role",
+ "label": "Assume_Role",
+ "category": "MANAGE"
}
],
diff --git a/plugin-ozone/pom.xml b/plugin-ozone/pom.xml
index be1ba2370..cc5398e59 100644
--- a/plugin-ozone/pom.xml
+++ b/plugin-ozone/pom.xml
@@ -145,9 +145,21 @@ limitations under the License.
<version>${org.bouncycastle.bcpkix-jdk18on}</version>
</dependency>
<dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>log4j-over-slf4j</artifactId>
- <version>${slf4j.version}</version>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-api</artifactId>
+ <version>${junit.jupiter.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-inline</artifactId>
+ <version>${mockito.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-junit-jupiter</artifactId>
+ <version>${mockito.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
diff --git
a/plugin-ozone/src/main/java/org/apache/ranger/authorization/ozone/authorizer/RangerOzoneAuthorizer.java
b/plugin-ozone/src/main/java/org/apache/ranger/authorization/ozone/authorizer/RangerOzoneAuthorizer.java
index 3b80da65b..03ac8d67a 100644
---
a/plugin-ozone/src/main/java/org/apache/ranger/authorization/ozone/authorizer/RangerOzoneAuthorizer.java
+++
b/plugin-ozone/src/main/java/org/apache/ranger/authorization/ozone/authorizer/RangerOzoneAuthorizer.java
@@ -20,6 +20,10 @@
package org.apache.ranger.authorization.ozone.authorizer;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.ozone.om.exceptions.OMException;
+import org.apache.hadoop.ozone.security.acl.AssumeRoleRequest;
+import org.apache.hadoop.ozone.security.acl.AssumeRoleRequest.OzoneGrant;
import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer;
import org.apache.hadoop.ozone.security.acl.IOzoneObj;
import org.apache.hadoop.ozone.security.acl.OzoneObj;
@@ -27,30 +31,45 @@
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.thirdparty.com.google.common.collect.Sets;
import org.apache.ranger.audit.provider.MiscUtil;
+import org.apache.ranger.authz.util.RangerResourceNameParser;
import org.apache.ranger.plugin.audit.RangerDefaultAuditHandler;
+import org.apache.ranger.plugin.model.RangerInlinePolicy;
+import org.apache.ranger.plugin.model.RangerPrincipal;
import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl;
import org.apache.ranger.plugin.policyengine.RangerAccessResourceImpl;
import org.apache.ranger.plugin.policyengine.RangerAccessResult;
import org.apache.ranger.plugin.service.RangerBasePlugin;
+import org.apache.ranger.plugin.util.JsonUtilsV2;
import org.apache.ranger.plugin.util.RangerPerfTracer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.Collections;
import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
public class RangerOzoneAuthorizer implements IAccessAuthorizer {
- public static final String ACCESS_TYPE_READ = "read";
- public static final String ACCESS_TYPE_WRITE = "write";
- public static final String ACCESS_TYPE_CREATE = "create";
- public static final String ACCESS_TYPE_LIST = "list";
- public static final String ACCESS_TYPE_DELETE = "delete";
- public static final String ACCESS_TYPE_READ_ACL = "read_acl";
- public static final String ACCESS_TYPE_WRITE_ACL = "write_acl";
+ public static final String ACCESS_TYPE_READ = "read";
+ public static final String ACCESS_TYPE_WRITE = "write";
+ public static final String ACCESS_TYPE_CREATE = "create";
+ public static final String ACCESS_TYPE_LIST = "list";
+ public static final String ACCESS_TYPE_DELETE = "delete";
+ public static final String ACCESS_TYPE_READ_ACL = "read_acl";
+ public static final String ACCESS_TYPE_WRITE_ACL = "write_acl";
+ public static final String ACCESS_TYPE_ASSUME_ROLE = "assume_role";
+ public static final String ACCESS_TYPE_ALL = "all";
-
- public static final String KEY_RESOURCE_VOLUME = "volume";
+ public static final String KEY_RESOURCE_VOLUME = "volume";
public static final String KEY_RESOURCE_BUCKET = "bucket";
- public static final String KEY_RESOURCE_KEY = "key";
+ public static final String KEY_RESOURCE_KEY = "key";
+ public static final String KEY_RESOURCE_ROLE = "role";
+
+ private static final String S3_VOLUME_NAME = "s3Vol";
private static final Logger PERF_OZONEAUTH_REQUEST_LOG =
RangerPerfTracer.getPerfLogger("ozoneauth.request");
@@ -78,6 +97,10 @@ public RangerOzoneAuthorizer() {
}
}
+ RangerOzoneAuthorizer(RangerBasePlugin plugin) {
+ rangerPlugin = plugin;
+ }
+
@Override
public boolean checkAccess(IOzoneObj ozoneObject, RequestContext
context) {
boolean returnValue = false;
@@ -149,11 +172,7 @@ public boolean checkAccess(IOzoneObj ozoneObject,
RequestContext context) {
if (ozoneObj.getResourceType() == OzoneObj.ResourceType.VOLUME)
{
rangerResource.setValue(KEY_RESOURCE_VOLUME,
ozoneObj.getVolumeName());
} else if (ozoneObj.getResourceType() ==
OzoneObj.ResourceType.BUCKET || ozoneObj.getResourceType() ==
OzoneObj.ResourceType.KEY) {
- if (ozoneObj.getStoreType() == OzoneObj.StoreType.S3) {
- rangerResource.setValue(KEY_RESOURCE_VOLUME,
"s3Vol");
- } else {
- rangerResource.setValue(KEY_RESOURCE_VOLUME,
ozoneObj.getVolumeName());
- }
+ rangerResource.setValue(KEY_RESOURCE_VOLUME,
ozoneObj.getStoreType() == OzoneObj.StoreType.S3 ? S3_VOLUME_NAME :
ozoneObj.getVolumeName());
rangerResource.setValue(KEY_RESOURCE_BUCKET,
ozoneObj.getBucketName());
if (ozoneObj.getResourceType() ==
OzoneObj.ResourceType.KEY) {
rangerResource.setValue(KEY_RESOURCE_KEY,
ozoneObj.getKeyName());
@@ -166,6 +185,10 @@ public boolean checkAccess(IOzoneObj ozoneObject,
RequestContext context) {
}
try {
+ if (StringUtils.isNotBlank(context.getSessionPolicy()))
{
+
rangerRequest.setInlinePolicy(JsonUtilsV2.jsonToObj(context.getSessionPolicy(),
RangerInlinePolicy.class));
+ }
+
RangerAccessResult result = plugin
.isAccessAllowed(rangerRequest);
if (result == null) {
@@ -186,6 +209,61 @@ public boolean checkAccess(IOzoneObj ozoneObject,
RequestContext context) {
return returnValue;
}
+ @Override
+ public String generateAssumeRoleSessionPolicy(AssumeRoleRequest
assumeRoleRequest) throws OMException {
+ LOG.debug("==>
RangerOzoneAuthorizer.generateAssumeRoleSessionPolicy(assumeRoleRequest={})",
assumeRoleRequest);
+
+ if (assumeRoleRequest == null) {
+ throw new OMException("invalid request: null",
OMException.ResultCodes.INVALID_REQUEST);
+ } else if (assumeRoleRequest.getClientUgi() == null) {
+ throw new OMException("invalid request: request.clientUgi null",
OMException.ResultCodes.INVALID_REQUEST);
+ } else if (assumeRoleRequest.getTargetRoleName() == null) {
+ throw new OMException("invalid request: request.targetRoleName
null", OMException.ResultCodes.INVALID_REQUEST);
+ }
+
+ RangerBasePlugin plugin = rangerPlugin;
+
+ if (plugin == null) {
+ throw new OMException("Ranger authorizer not initialized",
OMException.ResultCodes.INTERNAL_ERROR);
+ }
+
+ UserGroupInformation ugi = assumeRoleRequest.getClientUgi();
+ RangerAccessResourceImpl resource = new
RangerAccessResourceImpl(Collections.singletonMap(KEY_RESOURCE_ROLE,
assumeRoleRequest.getTargetRoleName()));
+ RangerAccessRequestImpl request = new
RangerAccessRequestImpl(resource, ACCESS_TYPE_ASSUME_ROLE,
ugi.getShortUserName(), Sets.newHashSet(ugi.getGroupNames()), null);
+
+ try {
+ RangerAccessResult result = plugin.isAccessAllowed(request);
+
+ if (result != null && result.getIsAccessDetermined() &&
result.getIsAllowed()) {
+ final Set<OzoneGrant> ozoneGrants =
assumeRoleRequest.getGrants();
+ final List<RangerInlinePolicy.Grant> inlineGrants;
+
+ if (ozoneGrants == null) { // allow all permissions
+ inlineGrants = null;
+ } else if (ozoneGrants.isEmpty()) { // don't allow any
permission
+ inlineGrants = Collections.singletonList(new
RangerInlinePolicy.Grant());
+ } else { // allow explicitly specified permissions
+ inlineGrants = ozoneGrants.stream().map(g ->
toRangerGrant(g, plugin)).collect(Collectors.toList());
+ }
+
+ RangerInlinePolicy inlinePolicy = new
RangerInlinePolicy(RangerPrincipal.PREFIX_ROLE +
assumeRoleRequest.getTargetRoleName(), RangerInlinePolicy.Mode.INLINE,
inlineGrants, ugi.getShortUserName());
+ String ret =
JsonUtilsV2.objToJson(inlinePolicy);
+
+ LOG.debug("<==
RangerOzoneAuthorizer.generateAssumeRoleSessionPolicy(assumeRoleRequest={}):
ret={}", assumeRoleRequest, ret);
+
+ return ret;
+ } else {
+ throw new OMException("Permission denied",
OMException.ResultCodes.ACCESS_DENIED);
+ }
+ } catch (OMException excp) {
+ throw excp;
+ } catch (Throwable t) {
+ LOG.error("isAccessAllowed() failed. request = {}", request, t);
+
+ throw new OMException("Ranger authorizer failed", t,
OMException.ResultCodes.INTERNAL_ERROR);
+ }
+ }
+
private String mapToRangerAccessType(ACLType operation) {
String rangerAccessType = null;
switch (operation) {
@@ -213,5 +291,84 @@ private String mapToRangerAccessType(ACLType operation) {
}
return rangerAccessType;
}
+
+ private static RangerInlinePolicy.Grant toRangerGrant(OzoneGrant
ozoneGrant, RangerBasePlugin plugin) {
+ RangerInlinePolicy.Grant ret = new RangerInlinePolicy.Grant();
+
+ if (ozoneGrant.getObjects() != null) {
+ ret.setResources(ozoneGrant.getObjects().stream().map(o ->
toRrn(o, plugin)).filter(Objects::nonNull).collect(Collectors.toSet()));
+ }
+
+ if (ozoneGrant.getPermissions() != null) {
+
ret.setPermissions(ozoneGrant.getPermissions().stream().map(RangerOzoneAuthorizer::toRangerPermission).filter(Objects::nonNull).collect(Collectors.toSet()));
+ }
+
+ LOG.debug("toRangerGrant(ozoneGrant={}): ret={}", ozoneGrant, ret);
+
+ return ret;
+ }
+
+ private static String toRrn(IOzoneObj obj, RangerBasePlugin plugin) {
+ OzoneObj ozoneObj = (OzoneObj) obj;
+ Map<String, String> resource = new HashMap<>();
+ String resType = null;
+
+ switch (ozoneObj.getResourceType()) {
+ case VOLUME:
+ resType = KEY_RESOURCE_VOLUME;
+
+ resource.put(KEY_RESOURCE_VOLUME, ozoneObj.getVolumeName());
+ break;
+
+ case BUCKET:
+ resType = KEY_RESOURCE_BUCKET;
+
+ resource.put(KEY_RESOURCE_VOLUME, ozoneObj.getStoreType() ==
OzoneObj.StoreType.S3 ? S3_VOLUME_NAME : ozoneObj.getVolumeName());
+ resource.put(KEY_RESOURCE_BUCKET, ozoneObj.getBucketName());
+ break;
+
+ case KEY:
+ resType = KEY_RESOURCE_KEY;
+
+ resource.put(KEY_RESOURCE_VOLUME, ozoneObj.getStoreType() ==
OzoneObj.StoreType.S3 ? S3_VOLUME_NAME : ozoneObj.getVolumeName());
+ resource.put(KEY_RESOURCE_BUCKET, ozoneObj.getBucketName());
+ resource.put(KEY_RESOURCE_KEY, ozoneObj.getKeyName());
+ break;
+ }
+
+ RangerResourceNameParser rrnParser = resType != null ?
plugin.getServiceDefHelper().getRrnParser(resType) : null;
+ String ret = rrnParser != null ? (resType +
RangerResourceNameParser.RRN_RESOURCE_TYPE_SEP +
rrnParser.toResourceName(resource)) : null;
+
+ LOG.debug("toRrn(ozoneObj={}): ret={}", ozoneObj, ret);
+
+ return ret;
+ }
+
+
+ private static String toRangerPermission(ACLType acl) {
+ switch (acl) {
+ case READ:
+ return ACCESS_TYPE_READ;
+ case WRITE:
+ return ACCESS_TYPE_WRITE;
+ case CREATE:
+ return ACCESS_TYPE_CREATE;
+ case LIST:
+ return ACCESS_TYPE_LIST;
+ case DELETE:
+ return ACCESS_TYPE_DELETE;
+ case READ_ACL:
+ return ACCESS_TYPE_READ_ACL;
+ case WRITE_ACL:
+ return ACCESS_TYPE_WRITE_ACL;
+ case ALL:
+ return ACCESS_TYPE_ALL;
+ case NONE:
+ case ASSUME_ROLE: // ASSUME_ROLE is not supported in session policy
+ return null;
+ }
+
+ return null;
+ }
}
diff --git
a/plugin-ozone/src/test/java/org/apache/ranger/authorization/ozone/authorizer/TestRangerOzoneAuthorizer.java
b/plugin-ozone/src/test/java/org/apache/ranger/authorization/ozone/authorizer/TestRangerOzoneAuthorizer.java
new file mode 100644
index 000000000..df3aa65ac
--- /dev/null
+++
b/plugin-ozone/src/test/java/org/apache/ranger/authorization/ozone/authorizer/TestRangerOzoneAuthorizer.java
@@ -0,0 +1,196 @@
+/*
+ * 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.ranger.authorization.ozone.authorizer;
+
+import org.apache.hadoop.ozone.om.exceptions.OMException;
+import org.apache.hadoop.ozone.security.acl.AssumeRoleRequest;
+import org.apache.hadoop.ozone.security.acl.AssumeRoleRequest.OzoneGrant;
+import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer;
+import org.apache.hadoop.ozone.security.acl.OzoneObj;
+import org.apache.hadoop.ozone.security.acl.OzoneObjInfo;
+import org.apache.hadoop.ozone.security.acl.RequestContext;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig;
+import org.apache.ranger.plugin.model.RangerInlinePolicy;
+import org.apache.ranger.plugin.service.RangerBasePlugin;
+import org.apache.ranger.plugin.util.JsonUtilsV2;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.net.InetAddress;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class TestRangerOzoneAuthorizer {
+ private static final String RANGER_SERVICE_TYPE = "ozone";
+ private static final String RANGER_APP_ID = "om";
+ private static final String OZONE_SERVICE_ID = "om";
+ private static final String OWNER_NAME = "ozone";
+
+ private static RangerOzoneAuthorizer ozoneAuthorizer;
+
+ private final String hostname = "localhost";
+ private final InetAddress ipAddress =
InetAddress.getLoopbackAddress();
+ private final UserGroupInformation user1 =
UserGroupInformation.createRemoteUser("user1");
+ private final UserGroupInformation user2 =
UserGroupInformation.createRemoteUser("user2");
+ private final String role1 = "role1";
+
+ private final OzoneObj vol1 = new
OzoneObjInfo.Builder().setResType(OzoneObj.ResourceType.VOLUME).setStoreType(OzoneObj.StoreType.OZONE).setVolumeName("vol1").build();
+ private final OzoneObj buck1 = new
OzoneObjInfo.Builder().setResType(OzoneObj.ResourceType.BUCKET).setStoreType(OzoneObj.StoreType.OZONE).setVolumeName("vol1").setBucketName("buck1").build();
+ private final OzoneObj key1 = new
OzoneObjInfo.Builder().setResType(OzoneObj.ResourceType.KEY).setStoreType(OzoneObj.StoreType.OZONE).setVolumeName("vol1").setBucketName("buck1").setKeyName("key1").build();
+ private final OzoneObj vol2 = new
OzoneObjInfo.Builder().setResType(OzoneObj.ResourceType.VOLUME).setStoreType(OzoneObj.StoreType.OZONE).setVolumeName("vol2").build();
+ private final OzoneGrant grantList = new OzoneGrant(new
HashSet<>(Arrays.asList(vol1, buck1)),
Collections.singleton(IAccessAuthorizer.ACLType.LIST));
+ private final OzoneGrant grantRead = new
OzoneGrant(Collections.singleton(key1),
Collections.singleton(IAccessAuthorizer.ACLType.READ));
+
+ @BeforeAll
+ public static void setUpBeforeClass() {
+ RangerPluginConfig pluginConfig = new
RangerPluginConfig(RANGER_SERVICE_TYPE, null, RANGER_APP_ID, null, null, null);
// loads ranger-ozone-security.xml
+ RangerBasePlugin plugin = new RangerBasePlugin(pluginConfig);
+
+ // loads policies from om_dev_ozone.json, by
EmbeddedResourcePolicySource configured in ranger-ozone-security.xml
+ plugin.init();
+
+ ozoneAuthorizer = new RangerOzoneAuthorizer(plugin);
+
+ assertNotNull(ozoneAuthorizer);
+ }
+
+ @Test
+ public void testAssumeRoleDeny() {
+ // user2 should not be allowed to assume role1 - no Ranger policy
grants this permission
+ AssumeRoleRequest request = new AssumeRoleRequest(hostname, ipAddress,
user2, role1, null);
+
+ assertThrows(OMException.class, () ->
ozoneAuthorizer.generateAssumeRoleSessionPolicy(request));
+ }
+
+ @Test
+ public void testAssumeRoleWithEmptyGrants() throws Exception {
+ Set<OzoneGrant> grants = Collections.emptySet();
+ AssumeRoleRequest request = new AssumeRoleRequest(hostname, ipAddress,
user1, role1, grants);
+
+ // user1 should be allowed to assume role1 - Ranger policy #100 grants
this permission
+ String sessionPolicy =
ozoneAuthorizer.generateAssumeRoleSessionPolicy(request);
+
+ assertNotNull(sessionPolicy);
+ assertNotEquals("", sessionPolicy);
+
+ RangerInlinePolicy inlinePolicy = JsonUtilsV2.jsonToObj(sessionPolicy,
RangerInlinePolicy.class);
+
+ assertEquals("r:role1", inlinePolicy.getGrantor());
+ assertEquals("user1", inlinePolicy.getCreatedBy());
+ assertEquals(RangerInlinePolicy.Mode.INLINE, inlinePolicy.getMode());
+ assertNotNull(inlinePolicy.getGrants());
+
+ RequestContext ctxListWithoutSessionPolicy = new
RequestContext(hostname, ipAddress, user1, OZONE_SERVICE_ID,
IAccessAuthorizer.ACLIdentityType.ANONYMOUS, IAccessAuthorizer.ACLType.LIST,
OWNER_NAME);
+ RequestContext ctxReadWithoutSessionPolicy = new
RequestContext(hostname, ipAddress, user1, OZONE_SERVICE_ID,
IAccessAuthorizer.ACLIdentityType.ANONYMOUS, IAccessAuthorizer.ACLType.READ,
OWNER_NAME);
+ RequestContext ctxListWithSessionPolicy = new
RequestContext(hostname, ipAddress, user1, OZONE_SERVICE_ID,
IAccessAuthorizer.ACLIdentityType.ANONYMOUS, IAccessAuthorizer.ACLType.LIST,
OWNER_NAME, false, sessionPolicy);
+ RequestContext ctxReadWithSessionPolicy = new
RequestContext(hostname, ipAddress, user1, OZONE_SERVICE_ID,
IAccessAuthorizer.ACLIdentityType.ANONYMOUS, IAccessAuthorizer.ACLType.READ,
OWNER_NAME, false, sessionPolicy);
+
+ // user1 doesn't have access without session-policy
+ assertFalse(ozoneAuthorizer.checkAccess(vol1,
ctxListWithoutSessionPolicy), "session-policy should not allow list on volume
vol1");
+ assertFalse(ozoneAuthorizer.checkAccess(vol2,
ctxListWithoutSessionPolicy), "session-policy should not allow list on volume
vol2");
+ assertFalse(ozoneAuthorizer.checkAccess(buck1,
ctxListWithoutSessionPolicy), "session-policy should not allow list on bucket
vol1/buck1");
+ assertFalse(ozoneAuthorizer.checkAccess(key1,
ctxReadWithoutSessionPolicy), "session-policy should not allow read on key
vol1/buck1/key1");
+
+ // user1 should not have access with session-policy as well, due to
empty grants
+ assertFalse(ozoneAuthorizer.checkAccess(vol1,
ctxListWithSessionPolicy), "session-policy should not allow list on volume
vol1");
+ assertFalse(ozoneAuthorizer.checkAccess(vol2,
ctxListWithSessionPolicy), "session-policy should not allow list on volume
vol2");
+ assertFalse(ozoneAuthorizer.checkAccess(buck1,
ctxListWithSessionPolicy), "session-policy should not allow list on bucket
vol1/buck1");
+ assertFalse(ozoneAuthorizer.checkAccess(key1,
ctxReadWithSessionPolicy), "session-policy should not allow read on key
vol1/buck1/key1");
+ }
+
+ @Test
+ public void testAssumeRoleWithNullGrants() throws Exception {
+ Set<OzoneGrant> grants = null;
+ AssumeRoleRequest request = new AssumeRoleRequest(hostname, ipAddress,
user1, role1, grants);
+
+ // user1 should be allowed to assume role1 - Ranger policy #100 grants
this permission
+ String sessionPolicy =
ozoneAuthorizer.generateAssumeRoleSessionPolicy(request);
+
+ assertNotNull(sessionPolicy);
+ assertNotEquals("", sessionPolicy);
+
+ RangerInlinePolicy inlinePolicy = JsonUtilsV2.jsonToObj(sessionPolicy,
RangerInlinePolicy.class);
+
+ assertEquals("r:role1", inlinePolicy.getGrantor());
+ assertEquals("user1", inlinePolicy.getCreatedBy());
+ assertEquals(RangerInlinePolicy.Mode.INLINE, inlinePolicy.getMode());
+ assertNull(inlinePolicy.getGrants());
+
+ RequestContext ctxListWithoutSessionPolicy = new
RequestContext(hostname, ipAddress, user1, OZONE_SERVICE_ID,
IAccessAuthorizer.ACLIdentityType.ANONYMOUS, IAccessAuthorizer.ACLType.LIST,
OWNER_NAME);
+ RequestContext ctxReadWithoutSessionPolicy = new
RequestContext(hostname, ipAddress, user1, OZONE_SERVICE_ID,
IAccessAuthorizer.ACLIdentityType.ANONYMOUS, IAccessAuthorizer.ACLType.READ,
OWNER_NAME);
+ RequestContext ctxListWithSessionPolicy = new
RequestContext(hostname, ipAddress, user1, OZONE_SERVICE_ID,
IAccessAuthorizer.ACLIdentityType.ANONYMOUS, IAccessAuthorizer.ACLType.LIST,
OWNER_NAME, false, sessionPolicy);
+ RequestContext ctxReadWithSessionPolicy = new
RequestContext(hostname, ipAddress, user1, OZONE_SERVICE_ID,
IAccessAuthorizer.ACLIdentityType.ANONYMOUS, IAccessAuthorizer.ACLType.READ,
OWNER_NAME, false, sessionPolicy);
+
+ // user1 doesn't have access without session-policy
+ assertFalse(ozoneAuthorizer.checkAccess(vol1,
ctxListWithoutSessionPolicy), "session-policy should not allow list on volume
vol1");
+ assertFalse(ozoneAuthorizer.checkAccess(vol2,
ctxListWithoutSessionPolicy), "session-policy should not allow list on volume
vol2");
+ assertFalse(ozoneAuthorizer.checkAccess(buck1,
ctxListWithoutSessionPolicy), "session-policy should not allow list on bucket
vol1/buck1");
+ assertFalse(ozoneAuthorizer.checkAccess(key1,
ctxReadWithoutSessionPolicy), "session-policy should not allow read on key
vol1/buck1/key1");
+
+ // user1 should have access with session-policy, due to null grants
which allows all accesses granted to role1
+ assertTrue(ozoneAuthorizer.checkAccess(vol1,
ctxListWithSessionPolicy), "session-policy should allow list on volume vol1");
+ assertTrue(ozoneAuthorizer.checkAccess(vol2,
ctxListWithSessionPolicy), "session-policy should allow list on volume vol2");
+ assertTrue(ozoneAuthorizer.checkAccess(buck1,
ctxListWithSessionPolicy), "session-policy should allow list on bucket
vol1/buck1");
+ assertTrue(ozoneAuthorizer.checkAccess(key1,
ctxReadWithSessionPolicy), "session-policy should allow read on key
vol1/buck1/key1");
+ }
+
+ @Test
+ public void testAssumeRoleWithGrants() throws Exception {
+ Set<OzoneGrant> grants = new HashSet<>(Arrays.asList(grantList,
grantRead));
+ AssumeRoleRequest request = new AssumeRoleRequest(hostname, ipAddress,
user1, role1, grants);
+
+ // user1 should be allowed to assume role1 - Ranger policy #100 grants
this permission
+ String sessionPolicy =
ozoneAuthorizer.generateAssumeRoleSessionPolicy(request);
+
+ assertNotNull(sessionPolicy);
+ assertNotEquals("", sessionPolicy);
+
+ RangerInlinePolicy inlinePolicy = JsonUtilsV2.jsonToObj(sessionPolicy,
RangerInlinePolicy.class);
+
+ assertEquals("r:role1", inlinePolicy.getGrantor());
+ assertEquals("user1", inlinePolicy.getCreatedBy());
+ assertEquals(RangerInlinePolicy.Mode.INLINE, inlinePolicy.getMode());
+ assertNotNull(inlinePolicy.getGrants());
+ assertEquals(2, inlinePolicy.getGrants().size());
+
+ assertTrue(inlinePolicy.getGrants().contains(new
RangerInlinePolicy.Grant(null, new HashSet<>(Arrays.asList("volume:vol1",
"bucket:vol1/buck1")), Collections.singleton("list"))));
+ assertTrue(inlinePolicy.getGrants().contains(new
RangerInlinePolicy.Grant(null, Collections.singleton("key:vol1/buck1/key1"),
Collections.singleton("read"))));
+
+ RequestContext ctxListWithSessionPolicy = new RequestContext(hostname,
ipAddress, user1, OZONE_SERVICE_ID,
IAccessAuthorizer.ACLIdentityType.ANONYMOUS, IAccessAuthorizer.ACLType.LIST,
OWNER_NAME, false, sessionPolicy);
+ RequestContext ctxReadWithSessionPolicy = new RequestContext(hostname,
ipAddress, user1, OZONE_SERVICE_ID,
IAccessAuthorizer.ACLIdentityType.ANONYMOUS, IAccessAuthorizer.ACLType.READ,
OWNER_NAME, false, sessionPolicy);
+
+ // user1 should have access with sessionPolicy
+ assertTrue(ozoneAuthorizer.checkAccess(vol1,
ctxListWithSessionPolicy), "session-policy should allow list on volume vol1");
+ assertFalse(ozoneAuthorizer.checkAccess(vol2,
ctxListWithSessionPolicy), "session-policy should not allow list on volume
vol2");
+ assertTrue(ozoneAuthorizer.checkAccess(buck1,
ctxListWithSessionPolicy), "session-policy should allow list on bucket
vol1/buck1");
+ assertTrue(ozoneAuthorizer.checkAccess(key1,
ctxReadWithSessionPolicy), "session-policy should allow read on key
vol1/buck1/key1");
+ }
+}
diff --git a/plugin-ozone/src/test/resources/om_dev_ozone.json
b/plugin-ozone/src/test/resources/om_dev_ozone.json
new file mode 100644
index 000000000..e8d761801
--- /dev/null
+++ b/plugin-ozone/src/test/resources/om_dev_ozone.json
@@ -0,0 +1,46 @@
+{
+ "serviceName": "dev_ozone", "serviceId": 1,
+ "serviceDef": {
+ "id": 1, "name":"ozone",
+ "resources":[
+ { "name": "volume", "level": 1, "parent": "",
"matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher",
"matcherOptions": { "wildCard": true, "ignoreCase": true }, "label":
"Volume", "description": "Volume" },
+ { "name": "bucket", "level": 2, "parent": "volume",
"matcher":"org.apache.ranger.plugin.resourcematcher.RangerURLResourceMatcher",
"matcherOptions": { "wildCard": true, "ignoreCase": true }, "label":
"Bucket", "description": "Bucket" },
+ { "name": "key", "level": 3, "parent": "bucket",
"matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher",
"matcherOptions": { "wildCard": true, "ignoreCase": true }, "label": "Key",
"description": "Key" },
+ { "name": "role", "level": 4, "parent": "",
"matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher",
"matcherOptions": { "wildCard": true, "ignoreCase": false }, "label": "Role",
"description": "Role" }
+ ],
+ "accessTypes":[
+ { "name": "read", "label": "Read" },
+ { "name": "write", "label": "Write" },
+ { "name": "create", "label": "Create" },
+ { "name": "list", "label": "List" },
+ { "name": "delete", "label": "Delete" },
+ { "name": "read_acl", "label": "Read_ACL" },
+ { "name": "write_acl", "label": "Write_ACL" },
+ { "name": "all", "label": "All", "impliedGrants": [ "read",
"write", "create", "list", "delete", "read_acl", "write_acl" ] },
+ { "name": "assume_role", "label": "Assume_Role" }
+ ]
+ },
+ "policies": [
+ { "id": 100, "name": "role: role1", "isEnabled": true, "isAuditEnabled":
true,
+ "resources": { "role": { "values": [ "role1" ] } },
+ "policyItems":[
+ { "accesses": [ { "type": "assume_role" } ], "users": [ "user1" ] }
+ ]
+ },
+ { "id": 101, "name": "volume: vol1, bucket: vol1/buck1", "isEnabled":
true, "isAuditEnabled": true,
+ "resources": { "volume": { "values": [ "vol1", "vol2" ] } },
+ "additionalResources": [
+ { "volume": { "values": [ "vol1" ] }, "bucket": { "values": [ "buck1"
] } }
+ ],
+ "policyItems":[
+ { "accesses": [ { "type": "list" } ], "roles": [ "role1" ] }
+ ]
+ },
+ { "id": 102, "name": "key: vol1/buck1/key1", "isEnabled": true,
"isAuditEnabled": true,
+ "resources": { "volume": { "values": [ "vol1" ] }, "bucket": { "values":
[ "buck1" ] }, "key": { "values": [ "key1" ] } },
+ "policyItems":[
+ { "accesses": [ { "type": "read" } ], "roles": [ "role1" ] }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/plugin-ozone/src/test/resources/ranger-ozone-security.xml
b/plugin-ozone/src/test/resources/ranger-ozone-security.xml
new file mode 100644
index 000000000..05c47ff82
--- /dev/null
+++ b/plugin-ozone/src/test/resources/ranger-ozone-security.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
+<configuration xmlns:xi="http://www.w3.org/2001/XInclude">
+ <property>
+ <name>ranger.plugin.ozone.service.name</name>
+ <value>dev_ozone</value>
+ </property>
+
+ <property>
+ <name>ranger.plugin.ozone.policy.source.impl</name>
+
<value>org.apache.ranger.admin.client.EmbeddedResourcePolicySource</value>
+ </property>
+</configuration>
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index e3c8df342..815330253 100644
--- a/pom.xml
+++ b/pom.xml
@@ -177,7 +177,7 @@
<org.bouncycastle.bcpkix-jdk18on>1.79</org.bouncycastle.bcpkix-jdk18on>
<org.bouncycastle.bcprov-jdk18on>1.79</org.bouncycastle.bcprov-jdk18on>
<owasp-java-html-sanitizer.version>20211018.2</owasp-java-html-sanitizer.version>
- <ozone.version>1.4.0</ozone.version>
+ <ozone.version>2.1.0</ozone.version>
<paranamer.version>2.3</paranamer.version>
<poi.version>5.2.2</poi.version>
<!-- presto plugin deps -->
@@ -787,6 +787,11 @@
<name>jetbrains-intellij-dependencies</name>
<url>https://packages.jetbrains.team/maven/p/ij/intellij-dependencies</url>
</repository>
+ <repository>
+ <id>ozone-2.1.0-rc</id>
+ <name>ozone-2.1.0-rc</name>
+
<url>https://repository.apache.org/content/repositories/orgapacheozone-1061</url>
+ </repository>
</repositories>
<distributionManagement>
<repository>
diff --git
a/ranger-ozone-plugin-shim/src/main/java/org/apache/ranger/authorization/ozone/authorizer/RangerOzoneAuthorizer.java
b/ranger-ozone-plugin-shim/src/main/java/org/apache/ranger/authorization/ozone/authorizer/RangerOzoneAuthorizer.java
index 59851e0ec..188ff649d 100644
---
a/ranger-ozone-plugin-shim/src/main/java/org/apache/ranger/authorization/ozone/authorizer/RangerOzoneAuthorizer.java
+++
b/ranger-ozone-plugin-shim/src/main/java/org/apache/ranger/authorization/ozone/authorizer/RangerOzoneAuthorizer.java
@@ -20,6 +20,7 @@
package org.apache.ranger.authorization.ozone.authorizer;
import org.apache.hadoop.ozone.om.exceptions.OMException;
+import org.apache.hadoop.ozone.security.acl.AssumeRoleRequest;
import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer;
import org.apache.hadoop.ozone.security.acl.IOzoneObj;
import org.apache.hadoop.ozone.security.acl.RequestContext;
@@ -100,6 +101,21 @@ public boolean checkAccess(IOzoneObj ozoneObject,
RequestContext context) throws
return ret;
}
+ @Override
+ public String generateAssumeRoleSessionPolicy(AssumeRoleRequest
assumeRoleRequest) throws OMException {
+ LOG.debug("==>
RangerOzoneAuthorizer.generateAssumeRoleSessionPolicy()");
+
+ try {
+ activatePluginClassLoader();
+
+ return
ozoneAuthorizationProviderImpl.generateAssumeRoleSessionPolicy(assumeRoleRequest);
+ } finally {
+ deactivatePluginClassLoader();
+
+ LOG.debug("<==
RangerOzoneAuthorizer.generateAssumeRoleSessionPolicy()");
+ }
+ }
+
private void activatePluginClassLoader() {
if(rangerPluginClassLoader != null) {
rangerPluginClassLoader.activate();