blob: 23fc9246dbcbfa5f25bb635a16fe704567455041 [file] [log] [blame]
/**
* 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.atlas.authorize.simple;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.Map;
import org.apache.atlas.ApplicationProperties;
import org.apache.atlas.AtlasException;
import org.apache.atlas.authorize.AtlasAccessRequest;
import org.apache.atlas.authorize.AtlasActionTypes;
import org.apache.atlas.authorize.AtlasAuthorizationException;
import org.apache.atlas.authorize.AtlasAuthorizer;
import org.apache.atlas.authorize.AtlasResourceTypes;
import org.apache.atlas.utils.PropertiesUtil;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOCase;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.annotations.VisibleForTesting;
public final class SimpleAtlasAuthorizer implements AtlasAuthorizer {
public enum AtlasAccessorTypes {
USER, GROUP;
}
private static final Logger LOG = LoggerFactory.getLogger(SimpleAtlasAuthorizer.class);
private boolean isDebugEnabled = LOG.isDebugEnabled();
private final static String WILDCARD_ASTERISK = "*";
private final static String WILDCARDS = "*?";
private boolean optIgnoreCase = false;
private Map<String, Map<AtlasResourceTypes, List<String>>> userReadMap = null;
private Map<String, Map<AtlasResourceTypes, List<String>>> userWriteMap = null;
private Map<String, Map<AtlasResourceTypes, List<String>>> userUpdateMap = null;
private Map<String, Map<AtlasResourceTypes, List<String>>> userDeleteMap = null;
private Map<String, Map<AtlasResourceTypes, List<String>>> groupReadMap = null;
private Map<String, Map<AtlasResourceTypes, List<String>>> groupWriteMap = null;
private Map<String, Map<AtlasResourceTypes, List<String>>> groupUpdateMap = null;
private Map<String, Map<AtlasResourceTypes, List<String>>> groupDeleteMap = null;
public SimpleAtlasAuthorizer() {
}
@Override
public void init() {
if (isDebugEnabled) {
LOG.debug("==> SimpleAtlasAuthorizer init");
}
try {
PolicyUtil util = new PolicyUtil();
PolicyParser parser = new PolicyParser();
optIgnoreCase = Boolean.valueOf(PropertiesUtil.getProperty("optIgnoreCase", "false"));
if (isDebugEnabled) {
LOG.debug("Read from PropertiesUtil --> optIgnoreCase :: " + optIgnoreCase);
}
Configuration configuration = ApplicationProperties.get();
String policyStorePath = configuration.getString("atlas.auth.policy.file", System.getProperty("atlas.conf")+"/policy-store.txt");
if (isDebugEnabled) {
LOG.debug("Loading Apache Atlas policies from : " + policyStorePath);
}
List<String> policies = FileReaderUtil.readFile(policyStorePath);
List<PolicyDef> policyDef = parser.parsePolicies(policies);
userReadMap = util.createPermissionMap(policyDef, AtlasActionTypes.READ, AtlasAccessorTypes.USER);
userWriteMap = util.createPermissionMap(policyDef, AtlasActionTypes.CREATE, AtlasAccessorTypes.USER);
userUpdateMap = util.createPermissionMap(policyDef, AtlasActionTypes.UPDATE, AtlasAccessorTypes.USER);
userDeleteMap = util.createPermissionMap(policyDef, AtlasActionTypes.DELETE, AtlasAccessorTypes.USER);
groupReadMap = util.createPermissionMap(policyDef, AtlasActionTypes.READ, AtlasAccessorTypes.GROUP);
groupWriteMap = util.createPermissionMap(policyDef, AtlasActionTypes.CREATE, AtlasAccessorTypes.GROUP);
groupUpdateMap = util.createPermissionMap(policyDef, AtlasActionTypes.UPDATE, AtlasAccessorTypes.GROUP);
groupDeleteMap = util.createPermissionMap(policyDef, AtlasActionTypes.DELETE, AtlasAccessorTypes.GROUP);
if (isDebugEnabled) {
LOG.debug("\n\nUserReadMap :: " + userReadMap + "\nGroupReadMap :: " + groupReadMap);
LOG.debug("\n\nUserWriteMap :: " + userWriteMap + "\nGroupWriteMap :: " + groupWriteMap);
LOG.debug("\n\nUserUpdateMap :: " + userUpdateMap + "\nGroupUpdateMap :: " + groupUpdateMap);
LOG.debug("\n\nUserDeleteMap :: " + userDeleteMap + "\nGroupDeleteMap :: " + groupDeleteMap);
}
} catch (IOException | AtlasException e) {
if (LOG.isErrorEnabled()) {
LOG.error("SimpleAtlasAuthorizer could not be initialized properly due to : ", e);
}
}
}
@Override
public boolean isAccessAllowed(AtlasAccessRequest request) throws AtlasAuthorizationException {
if (isDebugEnabled) {
LOG.debug("==> SimpleAtlasAuthorizer isAccessAllowed");
LOG.debug("isAccessAllowd(" + request + ")");
}
String user = request.getUser();
Set<String> groups = request.getUserGroups();
AtlasActionTypes action = request.getAction();
String resource = request.getResource();
Set<AtlasResourceTypes> resourceTypes = request.getResourceTypes();
if (isDebugEnabled)
LOG.debug("Checking for :: \nUser :: " + user + "\nGroups :: " + groups + "\nAction :: " + action
+ "\nResource :: " + resource);
boolean isAccessAllowed = false;
boolean isUser = user == null ? false : true;
boolean isGroup = groups == null ? false : true;
if ((!isUser && !isGroup) || action == null || resource == null) {
if (isDebugEnabled) {
LOG.debug("Please check the formation AtlasAccessRequest.");
}
return isAccessAllowed;
} else {
if (isDebugEnabled) {
LOG.debug("checkAccess for Operation :: " + action + " on Resource " + resourceTypes + ":" + resource);
}
switch (action) {
case READ:
isAccessAllowed = checkAccess(user, resourceTypes, resource, userReadMap);
isAccessAllowed =
isAccessAllowed == false ? checkAccessForGroups(groups, resourceTypes, resource, groupReadMap)
: isAccessAllowed;
break;
case CREATE:
isAccessAllowed = checkAccess(user, resourceTypes, resource, userWriteMap);
isAccessAllowed =
isAccessAllowed == false ? checkAccessForGroups(groups, resourceTypes, resource, groupWriteMap)
: isAccessAllowed;
break;
case UPDATE:
isAccessAllowed = checkAccess(user, resourceTypes, resource, userUpdateMap);
isAccessAllowed =
isAccessAllowed == false
? checkAccessForGroups(groups, resourceTypes, resource, groupUpdateMap) : isAccessAllowed;
break;
case DELETE:
isAccessAllowed = checkAccess(user, resourceTypes, resource, userDeleteMap);
isAccessAllowed =
isAccessAllowed == false
? checkAccessForGroups(groups, resourceTypes, resource, groupDeleteMap) : isAccessAllowed;
break;
default:
if (isDebugEnabled) {
LOG.debug("Invalid Action " + action+"\nRaising AtlasAuthorizationException!!!");
}
throw new AtlasAuthorizationException("Invalid Action :: " + action);
}
}
if (isDebugEnabled) {
LOG.debug("<== SimpleAtlasAuthorizer isAccessAllowed = " + isAccessAllowed);
}
return isAccessAllowed;
}
private boolean checkAccess(String accessor, Set<AtlasResourceTypes> resourceTypes, String resource,
Map<String, Map<AtlasResourceTypes, List<String>>> map) {
if (isDebugEnabled) {
LOG.debug("==> SimpleAtlasAuthorizer checkAccess");
LOG.debug("Now checking access for accessor : " + accessor + "\nResource Types : " + resourceTypes
+ "\nResource : " + resource + "\nMap : " + map);
}
boolean result = true;
Map<AtlasResourceTypes, List<String>> rescMap = map.get(accessor);
if (rescMap != null) {
for (AtlasResourceTypes resourceType : resourceTypes) {
List<String> accessList = rescMap.get(resourceType);
if (isDebugEnabled) {
LOG.debug("\nChecking for resource : " + resource + " in list : " + accessList + "\n");
}
if (accessList != null) {
result = result && isMatch(resource, accessList);
} else {
result = false;
}
}
} else {
result = false;
if (isDebugEnabled)
LOG.debug("Key " + accessor + " missing. Returning with result : " + result);
}
if (isDebugEnabled) {
LOG.debug("Check for " + accessor + " :: " + result);
LOG.debug("<== SimpleAtlasAuthorizer checkAccess");
}
return result;
}
private boolean checkAccessForGroups(Set<String> groups, Set<AtlasResourceTypes> resourceType, String resource,
Map<String, Map<AtlasResourceTypes, List<String>>> map) {
boolean isAccessAllowed = false;
if (isDebugEnabled) {
LOG.debug("==> SimpleAtlasAuthorizer checkAccessForGroups");
}
for (String group : groups) {
isAccessAllowed = checkAccess(group, resourceType, resource, map);
if (isAccessAllowed) {
break;
}
}
if (isDebugEnabled) {
LOG.debug("<== SimpleAtlasAuthorizer checkAccessForGroups");
}
return isAccessAllowed;
}
private boolean resourceMatchHelper(List<String> policyResource) {
boolean isMatchAny = false;
if (isDebugEnabled) {
LOG.debug("==> SimpleAtlasAuthorizer resourceMatchHelper");
}
boolean optWildCard = true;
List<String> policyValues = new ArrayList<String>();
if (policyResource != null) {
boolean isWildCardPresent = !optWildCard;
for (String policyValue : policyResource) {
if (StringUtils.isEmpty(policyValue)) {
continue;
}
if (StringUtils.containsOnly(policyValue, WILDCARD_ASTERISK)) {
isMatchAny = true;
} else if (!isWildCardPresent && StringUtils.containsAny(policyValue, WILDCARDS)) {
isWildCardPresent = true;
}
policyValues.add(policyValue);
}
optWildCard = optWildCard && isWildCardPresent;
} else {
isMatchAny = false;
}
if (isDebugEnabled) {
LOG.debug("<== SimpleAtlasAuthorizer resourceMatchHelper");
}
return isMatchAny;
}
private boolean isMatch(String resource, List<String> policyValues) {
if (isDebugEnabled) {
LOG.debug("==> SimpleAtlasAuthorizer isMatch");
}
boolean isMatchAny = resourceMatchHelper(policyValues);
boolean isMatch = false;
boolean allValuesRequested = isAllValuesRequested(resource);
if (allValuesRequested || isMatchAny) {
isMatch = isMatchAny;
} else {
for (String policyValue : policyValues) {
if (policyValue.contains("*")) {
isMatch =
optIgnoreCase ? FilenameUtils.wildcardMatch(resource, policyValue, IOCase.INSENSITIVE)
: FilenameUtils.wildcardMatch(resource, policyValue, IOCase.SENSITIVE);
} else {
isMatch =
optIgnoreCase ? StringUtils.equalsIgnoreCase(resource, policyValue) : StringUtils.equals(
resource, policyValue);
}
if (isMatch) {
break;
}
}
}
if (isMatch == false) {
if (isDebugEnabled) {
StringBuilder sb = new StringBuilder();
sb.append("[");
for (String policyValue : policyValues) {
sb.append(policyValue);
sb.append(" ");
}
sb.append("]");
LOG.debug("AtlasDefaultResourceMatcher.isMatch returns FALSE, (resource=" + resource
+ ", policyValues=" + sb.toString() + ")");
}
}
if (isDebugEnabled) {
LOG.debug("<== SimpleAtlasAuthorizer isMatch(" + resource + "): " + isMatch);
}
return isMatch;
}
private boolean isAllValuesRequested(String resource) {
boolean result = StringUtils.isEmpty(resource) || WILDCARD_ASTERISK.equals(resource);
return result;
}
@Override
public void cleanUp() {
if (isDebugEnabled) {
LOG.debug("==> +SimpleAtlasAuthorizer cleanUp");
}
userReadMap = null;
userWriteMap = null;
userUpdateMap = null;
userDeleteMap = null;
groupReadMap = null;
groupWriteMap = null;
groupUpdateMap = null;
groupDeleteMap = null;
if (isDebugEnabled) {
LOG.debug("<== +SimpleAtlasAuthorizer cleanUp");
}
}
/*
* NOTE :: This method is added for setting the maps for testing purpose.
*/
@VisibleForTesting
public void setResourcesForTesting(Map<String, Map<AtlasResourceTypes, List<String>>> userMap,
Map<String, Map<AtlasResourceTypes, List<String>>> groupMap, AtlasActionTypes actionTypes) {
switch (actionTypes) {
case READ:
this.userReadMap = userMap;
this.groupReadMap = groupMap;
break;
case CREATE:
this.userWriteMap = userMap;
this.groupWriteMap = groupMap;
break;
case UPDATE:
this.userUpdateMap = userMap;
this.groupUpdateMap = groupMap;
break;
case DELETE:
this.userDeleteMap = userMap;
this.groupDeleteMap = groupMap;
break;
default:
if (isDebugEnabled) {
LOG.debug("No such action available");
}
break;
}
}
}