Skip to content

Commit

Permalink
RANGER-4820: Support authorization of multiple accesses grouped by ac…
Browse files Browse the repository at this point in the history
…cess groups in one policy engine call - #2
  • Loading branch information
kulkabhay committed Sep 24, 2024
1 parent 7ba69cb commit bc596e6
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -887,7 +887,7 @@ protected void evaluatePolicyItems(RangerAccessRequest request, RangerPolicyReso
}
}

RangerAccessResult compositeAccessResult = getCompositeAccessResult(request);
RangerAccessResult compositeAccessResult = getCompositeAccessResult(request, result);
if (compositeAccessResult != null) {
result.setAccessResultFrom(compositeAccessResult);
}
Expand Down Expand Up @@ -937,39 +937,54 @@ private RangerAccessResult deriveAccessResultFromGroup(RangerAccessRequest reque
return ret;
}

private RangerAccessResult getCompositeAccessResult(RangerAccessRequest request) {
RangerAccessResult ret = null;
Set<Set<String>> allAccessTypeGroups = RangerAccessRequestUtil.getAllRequestedAccessTypeGroups(request);
Set<String> allAccessTypes = RangerAccessRequestUtil.getAllRequestedAccessTypes(request);
private RangerAccessResult getCompositeAccessResult(RangerAccessRequest request, RangerAccessResult result) {
RangerAccessResult ret = null;
Set<Set<String>> allAccessTypeGroups = RangerAccessRequestUtil.getAllRequestedAccessTypeGroups(request);
Set<String> allAccessTypes = RangerAccessRequestUtil.getAllRequestedAccessTypes(request);
Set<String> ignoreIfNotDeniedAccessTypes = RangerAccessRequestUtil.getIgnoreIfNotDeniedAccessTypes(request);

if (CollectionUtils.isEmpty(allAccessTypeGroups)) {
ret = deriveAccessResultFromGroup(request, allAccessTypes);
if (ret == null && CollectionUtils.isNotEmpty(ignoreIfNotDeniedAccessTypes) && ignoreIfNotDeniedAccessTypes.containsAll(allAccessTypes)) {
// group does not allow/deny access and this group's all access-types are also in the ignore-if-not-denied access-type list
ret = new RangerAccessResult(result.getPolicyType(), result.getServiceName(), result.getServiceDef(), request);
ret.setAuditResultFrom(result);
ret.setIsAllowed(true);
}
} else {
boolean isAccessDetermined = true;
boolean isAccessAllowed = false;
RangerAccessResult allowResult = null;
boolean isAccessDetermined = true;
boolean isAccessAllowed = false;
RangerAccessResult allowResult = null;

for (Set<String> accessesInGroup : allAccessTypeGroups) {
RangerAccessResult groupResult = deriveAccessResultFromGroup(request, accessesInGroup);
if (groupResult != null) {
if (!groupResult.getIsAllowed()) {
// Deny
isAccessAllowed = false;
ret = groupResult;
isAccessAllowed = false;
ret = groupResult;
break;
} else {
isAccessAllowed = true;
isAccessAllowed = true;
if (allowResult == null) {
allowResult = groupResult;
}
}
} else {
// Some group is not completely authorized yet
isAccessDetermined = false;
if (!(CollectionUtils.isNotEmpty(ignoreIfNotDeniedAccessTypes) && ignoreIfNotDeniedAccessTypes.containsAll(accessesInGroup))) {
// group does not allow/deny access and this group's all access-types are also in the ignore-if-not-denied access-type list
isAccessDetermined = false;
}
}
}

if (isAccessDetermined && isAccessAllowed) {
ret = allowResult;
} else if (isAccessDetermined && ret == null) { // If none of the groups allowed/denied access and every group's all access-type results are to be ignored unless denied
ret = new RangerAccessResult(result.getPolicyType(), result.getServiceName(), result.getServiceDef(), request);
ret.setAuditResultFrom(result);
ret.setIsAllowed(true);
}
}
return ret;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,29 +34,30 @@
public class RangerAccessRequestUtil {
private static final Logger LOG = LoggerFactory.getLogger(RangerAccessRequestUtil.class);

public static final String KEY_CONTEXT_TAGS = "TAGS";
public static final String KEY_CONTEXT_TAG_OBJECT = "TAG_OBJECT";
public static final String KEY_CONTEXT_RESOURCE = "RESOURCE";
public static final String KEY_CONTEXT_REQUESTED_RESOURCES = "REQUESTED_RESOURCES";
public static final String KEY_CONTEXT_USERSTORE = "USERSTORE";
public static final String KEY_TOKEN_NAMESPACE = "token:";
public static final String KEY_USER = "USER";
public static final String KEY_OWNER = "OWNER";
public static final String KEY_ROLES = "ROLES";
public static final String KEY_CONTEXT_IS_ANY_ACCESS = "ISANYACCESS";
public static final String KEY_CONTEXT_ALL_ACCESSTYPE_GROUPS = "ALLACCESSTYPEGROUPS";
public static final String KEY_CONTEXT_ALL_ACCESSTYPES = "ALLACCESSTYPES";
public static final String KEY_CONTEXT_ALL_ACCESS_TYPE_RESULTS = "ALL_ACCESS_TYPE_RESULTS";
public static final String KEY_CONTEXT_ALL_ACCESS_TYPE_ACL_RESULTS = "ALL_ACCESS_TYPE_ACL_RESULTS";

public static final String KEY_CONTEXT_REQUEST = "_REQUEST";
public static final String KEY_CONTEXT_GDS_RESULT = "_GDS_RESULT";
public static final String KEY_CONTEXT_IS_REQUEST_PREPROCESSED = "ISREQUESTPREPROCESSED";
public static final String KEY_CONTEXT_RESOURCE_ZONE_NAMES = "RESOURCE_ZONE_NAMES";
public static final String KEY_CONTEXT_IS_SKIP_CHAINED_PLUGINS = "_IS_SKIP_CHAINED_PLUGINS";
public static final String KEY_CONTEXT_TAGS = "TAGS";
public static final String KEY_CONTEXT_TAG_OBJECT = "TAG_OBJECT";
public static final String KEY_CONTEXT_RESOURCE = "RESOURCE";
public static final String KEY_CONTEXT_REQUESTED_RESOURCES = "REQUESTED_RESOURCES";
public static final String KEY_CONTEXT_USERSTORE = "USERSTORE";
public static final String KEY_TOKEN_NAMESPACE = "token:";
public static final String KEY_USER = "USER";
public static final String KEY_OWNER = "OWNER";
public static final String KEY_ROLES = "ROLES";
public static final String KEY_CONTEXT_IS_ANY_ACCESS = "ISANYACCESS";
public static final String KEY_CONTEXT_ALL_ACCESSTYPE_GROUPS = "ALLACCESSTYPEGROUPS";
public static final String KEY_CONTEXT_ALL_ACCESSTYPES = "ALLACCESSTYPES";
public static final String KEY_CONTEXT_IGNORE_IF_NOT_DENIED_ACCESSTYPES = "IGNOREIFNOTDENIEDACCESSTYPES";
public static final String KEY_CONTEXT_ALL_ACCESS_TYPE_RESULTS = "ALL_ACCESS_TYPE_RESULTS";
public static final String KEY_CONTEXT_ALL_ACCESS_TYPE_ACL_RESULTS = "ALL_ACCESS_TYPE_ACL_RESULTS";

public static final String KEY_CONTEXT_REQUEST = "_REQUEST";
public static final String KEY_CONTEXT_GDS_RESULT = "_GDS_RESULT";
public static final String KEY_CONTEXT_IS_REQUEST_PREPROCESSED = "ISREQUESTPREPROCESSED";
public static final String KEY_CONTEXT_RESOURCE_ZONE_NAMES = "RESOURCE_ZONE_NAMES";
public static final String KEY_CONTEXT_IS_SKIP_CHAINED_PLUGINS = "_IS_SKIP_CHAINED_PLUGINS";

public static void setRequestTagsInContext(Map<String, Object> context, Set<RangerTagForEval> tags) {
if(CollectionUtils.isEmpty(tags)) {
if (CollectionUtils.isEmpty(tags)) {
context.remove(KEY_CONTEXT_TAGS);
} else {
context.put(KEY_CONTEXT_TAGS, tags);
Expand All @@ -65,12 +66,11 @@ public static void setRequestTagsInContext(Map<String, Object> context, Set<Rang

public static Set<RangerTagForEval> getRequestTagsFromContext(Map<String, Object> context) {
Set<RangerTagForEval> ret = null;
Object val = context.get(RangerAccessRequestUtil.KEY_CONTEXT_TAGS);
Object val = context.get(RangerAccessRequestUtil.KEY_CONTEXT_TAGS);

if (val instanceof Set<?>) {
try {
@SuppressWarnings("unchecked")
Set<RangerTagForEval> tags = (Set<RangerTagForEval>) val;
@SuppressWarnings("unchecked") Set<RangerTagForEval> tags = (Set<RangerTagForEval>) val;

ret = tags;
} catch (Throwable t) {
Expand All @@ -87,10 +87,10 @@ public static void setCurrentTagInContext(Map<String, Object> context, RangerTag

public static RangerTagForEval getCurrentTagFromContext(Map<String, Object> context) {
RangerTagForEval ret = null;
Object val = context.get(KEY_CONTEXT_TAG_OBJECT);
Object val = context.get(KEY_CONTEXT_TAG_OBJECT);

if(val instanceof RangerTagForEval) {
ret = (RangerTagForEval)val;
if (val instanceof RangerTagForEval) {
ret = (RangerTagForEval) val;
}

return ret;
Expand All @@ -104,8 +104,8 @@ public static RangerRequestedResources getRequestedResourcesFromContext(Map<Stri
RangerRequestedResources ret = null;
Object val = context.get(KEY_CONTEXT_REQUESTED_RESOURCES);

if(val instanceof RangerRequestedResources) {
ret = (RangerRequestedResources)val;
if (val instanceof RangerRequestedResources) {
ret = (RangerRequestedResources) val;
}

return ret;
Expand All @@ -119,8 +119,8 @@ public static RangerAccessResource getCurrentResourceFromContext(Map<String, Obj
RangerAccessResource ret = null;
Object val = MapUtils.isNotEmpty(context) ? context.get(KEY_CONTEXT_RESOURCE) : null;

if(val instanceof RangerAccessResource) {
ret = (RangerAccessResource)val;
if (val instanceof RangerAccessResource) {
ret = (RangerAccessResource) val;
}

return ret;
Expand All @@ -129,7 +129,7 @@ public static RangerAccessResource getCurrentResourceFromContext(Map<String, Obj
public static Map<String, Object> copyContext(Map<String, Object> context) {
final Map<String, Object> ret;

if(MapUtils.isEmpty(context)) {
if (MapUtils.isEmpty(context)) {
ret = new HashMap<>();
} else {
ret = new HashMap<>(context);
Expand All @@ -145,6 +145,7 @@ public static Map<String, Object> copyContext(Map<String, Object> context) {
ret.remove(KEY_CONTEXT_ALL_ACCESS_TYPE_RESULTS);
ret.remove(KEY_CONTEXT_ALL_ACCESS_TYPE_ACL_RESULTS);
ret.remove(KEY_CONTEXT_IS_REQUEST_PREPROCESSED);
ret.remove(KEY_CONTEXT_IGNORE_IF_NOT_DENIED_ACCESSTYPES);
// don't remove REQUESTED_RESOURCES
}

Expand All @@ -154,9 +155,11 @@ public static Map<String, Object> copyContext(Map<String, Object> context) {
public static void setCurrentUserInContext(Map<String, Object> context, String user) {
setTokenInContext(context, KEY_USER, user);
}

public static void setOwnerInContext(Map<String, Object> context, String owner) {
setTokenInContext(context, KEY_OWNER, owner);
}

public static String getCurrentUserFromContext(Map<String, Object> context) {
Object ret = getTokenFromContext(context, KEY_USER);
return ret != null ? ret.toString() : "";
Expand All @@ -166,21 +169,22 @@ public static void setTokenInContext(Map<String, Object> context, String tokenNa
String tokenNameWithNamespace = KEY_TOKEN_NAMESPACE + tokenName;
context.put(tokenNameWithNamespace, tokenValue);
}

public static Object getTokenFromContext(Map<String, Object> context, String tokenName) {
String tokenNameWithNamespace = KEY_TOKEN_NAMESPACE + tokenName;
return MapUtils.isNotEmpty(context) ? context.get(tokenNameWithNamespace) : null;
}

public static void setCurrentUserRolesInContext(Map<String, Object> context, Set<String> roles) {
setTokenInContext(context, KEY_ROLES, roles);
}
public static void setCurrentUserRolesInContext(Map<String, Object> context, Set<String> roles) {
setTokenInContext(context, KEY_ROLES, roles);
}

public static Set<String> getCurrentUserRolesFromContext(Map<String, Object> context) {
Object ret = getTokenFromContext(context, KEY_ROLES);
return ret != null ? (Set<String>) ret : Collections.EMPTY_SET;
}
public static Set<String> getCurrentUserRolesFromContext(Map<String, Object> context) {
Object ret = getTokenFromContext(context, KEY_ROLES);
return ret != null ? (Set<String>) ret : Collections.EMPTY_SET;
}

public static Set<String> getUserRoles(RangerAccessRequest request) {
public static Set<String> getUserRoles(RangerAccessRequest request) {
Set<String> ret = Collections.EMPTY_SET;

if (request != null) {
Expand All @@ -200,9 +204,9 @@ public static void setRequestUserStoreInContext(Map<String, Object> context, Ran

public static RangerUserStore getRequestUserStoreFromContext(Map<String, Object> context) {
RangerUserStore ret = null;
Object val = context.get(KEY_CONTEXT_USERSTORE);
Object val = context.get(KEY_CONTEXT_USERSTORE);

if(val instanceof RangerUserStore) {
if (val instanceof RangerUserStore) {
ret = (RangerUserStore) val;
}

Expand All @@ -214,7 +218,7 @@ public static void setIsAnyAccessInContext(Map<String, Object> context, Boolean
}

public static boolean getIsAnyAccessInContext(Map<String, Object> context) {
Boolean value = (Boolean)context.get(KEY_CONTEXT_IS_ANY_ACCESS);
Boolean value = (Boolean) context.get(KEY_CONTEXT_IS_ANY_ACCESS);
return value != null && value;
}

Expand All @@ -223,14 +227,36 @@ public static void setIsRequestPreprocessed(Map<String, Object> context, Boolean
}

public static boolean getIsRequestPreprocessed(Map<String, Object> context) {
Boolean value = (Boolean)context.get(KEY_CONTEXT_IS_REQUEST_PREPROCESSED);
Boolean value = (Boolean) context.get(KEY_CONTEXT_IS_REQUEST_PREPROCESSED);
return value != null && value;
}

public static void setAllRequestedAccessTypes(Map<String, Object> context, Set<String> accessTypes) {
context.put(KEY_CONTEXT_ALL_ACCESSTYPES, accessTypes);
}

public static void setIgnoreIfNotDeniedAccessTypes(Map<String, Object> context, Set<String> accessTypes) {
context.put(KEY_CONTEXT_IGNORE_IF_NOT_DENIED_ACCESSTYPES, accessTypes);
}

public static Set<String> getIgnoreIfNotDeniedAccessTypes(RangerAccessRequest request) {
Set<String> ret = Collections.emptySet();

Object val = request.getContext().get(KEY_CONTEXT_IGNORE_IF_NOT_DENIED_ACCESSTYPES);

if (val != null) {
if (val instanceof Set<?>) {
ret = (Set<String>) val;
} else if (val instanceof List<?>) {
ret = new TreeSet<>((List<String>) val);
} else {
LOG.error("getNotDeniedRequestedAccessTypes(): failed to get NOTDENIEDACCESSTYPES from context");
}
}
return ret;
}


@SuppressWarnings("unchecked")
public static Set<String> getAllRequestedAccessTypes(RangerAccessRequest request) {
Set<String> ret = null;
Expand All @@ -251,7 +277,7 @@ public static Set<String> getAllRequestedAccessTypes(RangerAccessRequest request
}

public static Set<Set<String>> getAllRequestedAccessTypeGroups(RangerAccessRequest request) {
Object val = request.getContext().get(KEY_CONTEXT_ALL_ACCESSTYPE_GROUPS);
Object val = request.getContext().get(KEY_CONTEXT_ALL_ACCESSTYPE_GROUPS);
return (Set<Set<String>>) val;
}

Expand All @@ -265,7 +291,7 @@ public static void setAllRequestedAccessTypeGroups(RangerAccessRequest request,

public static Map<String, RangerAccessResult> getAccessTypeResults(RangerAccessRequest request) {
Map<String, RangerAccessResult> ret;
Object val = request.getContext().get(KEY_CONTEXT_ALL_ACCESS_TYPE_RESULTS);
Object val = request.getContext().get(KEY_CONTEXT_ALL_ACCESS_TYPE_RESULTS);
if (val == null) {
ret = new HashMap<>();
request.getContext().put(KEY_CONTEXT_ALL_ACCESS_TYPE_RESULTS, ret);
Expand All @@ -277,7 +303,7 @@ public static Map<String, RangerAccessResult> getAccessTypeResults(RangerAccessR

public static Map<String, Integer> getAccessTypeACLResults(RangerAccessRequest request) {
Map<String, Integer> ret;
Object val = request.getContext().get(KEY_CONTEXT_ALL_ACCESS_TYPE_ACL_RESULTS);
Object val = request.getContext().get(KEY_CONTEXT_ALL_ACCESS_TYPE_ACL_RESULTS);
if (val == null) {
ret = new HashMap<>();
request.getContext().put(KEY_CONTEXT_ALL_ACCESS_TYPE_ACL_RESULTS, ret);
Expand Down Expand Up @@ -400,7 +426,7 @@ public static Map<String, RangerAccessResult> getAccessTypeResults(Map<String, O
if (context != null) {
Object o = context.get(KEY_CONTEXT_ALL_ACCESS_TYPE_RESULTS);
if (o != null) {
ret = (Map<String, RangerAccessResult>)o;
ret = (Map<String, RangerAccessResult>) o;
}
}

Expand All @@ -426,7 +452,7 @@ public static void setIsSkipChainedPlugins(Map<String, Object> context, Boolean
}

public static boolean getIsSkipChainedPlugins(Map<String, Object> context) {
Boolean value = (Boolean)context.get(KEY_CONTEXT_IS_SKIP_CHAINED_PLUGINS);
Boolean value = (Boolean) context.get(KEY_CONTEXT_IS_SKIP_CHAINED_PLUGINS);
return value != null && value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -899,6 +899,9 @@ private AuthzStatus isAccessAllowed(INode inode, INodeAttributes inodeAttribs, S

RangerAccessRequestUtil.setAllRequestedAccessTypeGroups(request, allAccessTypeGroups);
RangerAccessRequestUtil.setAllRequestedAccessTypes(request.getContext(), accessTypes);
if (accessTypes.contains(EXECUTE_ACCCESS_TYPE)) {
RangerAccessRequestUtil.setIgnoreIfNotDeniedAccessTypes(request.getContext(), access2ActionListMapper.get(FsAction.EXECUTE));
}
}

// if the request was already allowed by a Ranger policy (for ancestor/parent/node/child), skip chained plugin evaluations in subsequent calls
Expand Down Expand Up @@ -979,6 +982,9 @@ private AuthzStatus isAccessAllowedForHierarchy(INode inode, INodeAttributes ino

RangerAccessRequestUtil.setAllRequestedAccessTypeGroups(request, allAccessTypeGroups);
RangerAccessRequestUtil.setAllRequestedAccessTypes(request.getContext(), accessTypes);
if (accessTypes.contains(EXECUTE_ACCCESS_TYPE)) {
RangerAccessRequestUtil.setIgnoreIfNotDeniedAccessTypes(request.getContext(), access2ActionListMapper.get(FsAction.EXECUTE));
}
}

RangerAccessResult result = context.plugin.isAccessAllowed(request, null);
Expand Down

0 comments on commit bc596e6

Please sign in to comment.