Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"uiHint":"",
"label": "Ozone Volume",
"description": "Ozone Volume",
"accessTypeRestrictions": [ "read", "write", "create", "list", "delete", "read_acl", "write_acl", "all" ],
"isValidLeaf": true
},
{
Expand All @@ -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",
Expand All @@ -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
}
],

Expand Down Expand Up @@ -130,6 +149,12 @@
"name": "write_acl",
"label": "Write_ACL",
"category": "UPDATE"
},
{
"itemId": 8,
"name": "assume_role",
"label": "Assume_Role",
"category": "MANAGE"
}
],

Expand Down
18 changes: 15 additions & 3 deletions plugin-ozone/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,38 +20,59 @@

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;
import org.apache.hadoop.ozone.security.acl.RequestContext;
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 {
private static final Logger LOG = LoggerFactory.getLogger(RangerOzoneAuthorizer.class);
private static final Logger PERF_OZONEAUTH_REQUEST_LOG = RangerPerfTracer.getPerfLogger("ozoneauth.request");

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 KEY_RESOURCE_VOLUME = "volume";
public static final String KEY_RESOURCE_BUCKET = "bucket";
public static final String KEY_RESOURCE_KEY = "key";
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_BUCKET = "bucket";
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 volatile RangerBasePlugin rangerPlugin;

Expand All @@ -77,6 +98,11 @@ public RangerOzoneAuthorizer() {
}
}

// for testing only
RangerOzoneAuthorizer(RangerBasePlugin plugin) {
rangerPlugin = plugin;
}

@Override
public boolean checkAccess(IOzoneObj ozoneObject, RequestContext context) {
boolean returnValue = false;
Expand Down Expand Up @@ -149,12 +175,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) {
Expand All @@ -170,6 +191,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) {
Expand All @@ -188,6 +213,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) {
final String rangerAccessType;

Expand Down Expand Up @@ -221,4 +301,82 @@ 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;
}
}
Loading
Loading