blob: 7f841d469a2bee6c5d0aa5d68c0d24f695dc6583 [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.ranger.authorization.atlas.authorizer;
import org.apache.atlas.authorize.AtlasAdminAccessRequest;
import org.apache.atlas.authorize.AtlasAuthorizationException;
import org.apache.atlas.authorize.AtlasEntityAccessRequest;
import org.apache.atlas.authorize.AtlasSearchResultScrubRequest;
import org.apache.atlas.authorize.AtlasRelationshipAccessRequest;
import org.apache.atlas.authorize.AtlasTypeAccessRequest;
import org.apache.atlas.authorize.AtlasAuthorizer;
import org.apache.atlas.authorize.AtlasPrivilege;
import org.apache.atlas.model.discovery.AtlasSearchResult;
import org.apache.atlas.model.instance.AtlasEntityHeader;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ranger.audit.model.AuthzAuditEvent;
import org.apache.ranger.plugin.audit.RangerDefaultAuditHandler;
import org.apache.ranger.plugin.model.RangerServiceDef;
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.RangerPerfTracer;
import java.util.*;
import static org.apache.ranger.services.atlas.RangerServiceAtlas.*;
public class RangerAtlasAuthorizer implements AtlasAuthorizer {
private static final Log LOG = LogFactory.getLog(RangerAtlasAuthorizer.class);
private static final Log PERF_LOG = RangerPerfTracer.getPerfLogger("atlasauth.request");
private static volatile RangerBasePlugin atlasPlugin = null;
@Override
public void init() {
if (LOG.isDebugEnabled()) {
LOG.debug("==> RangerAtlasPlugin.init()");
}
RangerBasePlugin plugin = atlasPlugin;
if (plugin == null) {
synchronized (RangerAtlasPlugin.class) {
plugin = atlasPlugin;
if (plugin == null) {
plugin = new RangerAtlasPlugin();
plugin.init();
plugin.setResultProcessor(new RangerDefaultAuditHandler(plugin.getConfig()));
atlasPlugin = plugin;
}
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("<== RangerAtlasPlugin.init()");
}
}
@Override
public void cleanUp() {
if (LOG.isDebugEnabled()) {
LOG.debug("==> cleanUp ");
}
}
@Override
public boolean isAccessAllowed(AtlasAdminAccessRequest request) throws AtlasAuthorizationException {
if (LOG.isDebugEnabled()) {
LOG.debug("==> isAccessAllowed(" + request + ")");
}
final boolean ret;
RangerPerfTracer perf = null;
try {
if (RangerPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = RangerPerfTracer.getPerfTracer(PERF_LOG, "RangerAtlasAuthorizer.isAccessAllowed(" + request + ")");
}
String action = request.getAction() != null ? request.getAction().getType() : null;
RangerAccessResourceImpl rangerResource = new RangerAccessResourceImpl(Collections.singletonMap(RESOURCE_SERVICE, "*"));
RangerAccessRequestImpl rangerRequest = new RangerAccessRequestImpl(rangerResource, action, request.getUser(), request.getUserGroups());
rangerRequest.setClientIPAddress(request.getClientIPAddress());
rangerRequest.setAccessTime(request.getAccessTime());
rangerRequest.setAction(action);
rangerRequest.setForwardedAddresses(request.getForwardedAddresses());
rangerRequest.setRemoteIPAddress(request.getRemoteIPAddress());
ret = checkAccess(rangerRequest);
} finally {
RangerPerfTracer.log(perf);
}
if (LOG.isDebugEnabled()) {
LOG.debug("<== isAccessAllowed(" + request + "): " + ret);
}
return ret;
}
@Override
public boolean isAccessAllowed(AtlasEntityAccessRequest request) throws AtlasAuthorizationException {
if (LOG.isDebugEnabled()) {
LOG.debug("==> isAccessAllowed(" + request + ")");
}
boolean ret = true;
RangerPerfTracer perf = null;
RangerAtlasAuditHandler auditHandler = null;
try {
if (RangerPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = RangerPerfTracer.getPerfTracer(PERF_LOG, "RangerAtlasAuthorizer.isAccessAllowed(" + request + ")");
}
// not initializing audit handler, so that audits are not logged when entity details are NULL or EMPTY STRING
if (!(StringUtils.isEmpty(request.getEntityId()) && request.getClassification() == null && request.getEntity() == null)) {
auditHandler = new RangerAtlasAuditHandler(request, getServiceDef());
}
ret = isAccessAllowed(request, auditHandler);
} finally {
RangerPerfTracer.log(perf);
}
if (LOG.isDebugEnabled()) {
LOG.debug("<== isAccessAllowed(" + request + "): " + ret);
}
return ret;
}
@Override
public boolean isAccessAllowed(AtlasTypeAccessRequest request) throws AtlasAuthorizationException {
if (LOG.isDebugEnabled()) {
LOG.debug("==> isAccessAllowed(" + request + ")");
}
final boolean ret;
RangerPerfTracer perf = null;
try {
if (RangerPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = RangerPerfTracer.getPerfTracer(PERF_LOG, "RangerAtlasAuthorizer.isAccessAllowed(" + request + ")");
}
final String typeName = request.getTypeDef() != null ? request.getTypeDef().getName() : null;
final String typeCategory = request.getTypeDef() != null && request.getTypeDef().getCategory() != null ? request.getTypeDef().getCategory().name() : null;
final String action = request.getAction() != null ? request.getAction().getType() : null;
RangerAccessResourceImpl rangerResource = new RangerAccessResourceImpl();
rangerResource.setValue(RESOURCE_TYPE_NAME, typeName);
rangerResource.setValue(RESOURCE_TYPE_CATEGORY, typeCategory);
RangerAccessRequestImpl rangerRequest = new RangerAccessRequestImpl(rangerResource, action, request.getUser(), request.getUserGroups());
rangerRequest.setClientIPAddress(request.getClientIPAddress());
rangerRequest.setAccessTime(request.getAccessTime());
rangerRequest.setAction(action);
rangerRequest.setForwardedAddresses(request.getForwardedAddresses());
rangerRequest.setRemoteIPAddress(request.getRemoteIPAddress());
ret = checkAccess(rangerRequest);
} finally {
RangerPerfTracer.log(perf);
}
if (LOG.isDebugEnabled()) {
LOG.debug("<== isAccessAllowed(" + request + "): " + ret);
}
return ret;
}
public boolean isAccessAllowed(AtlasRelationshipAccessRequest request) throws AtlasAuthorizationException {
if (LOG.isDebugEnabled()) {
LOG.debug("==> isAccessAllowed(" + request + ")");
}
boolean ret;
RangerPerfTracer perf = null;
try {
if (RangerPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = RangerPerfTracer.getPerfTracer(PERF_LOG, "RangerAtlasAuthorizer.isAccessAllowed(" + request + ")");
}
final String action = request.getAction() != null ? request.getAction().getType() : null;
final Set<String> end1EntityTypeAndSuperTypes = request.getEnd1EntityTypeAndAllSuperTypes();
final Set<String> end1Classifications = new HashSet<>(request.getEnd1EntityClassifications());
final String end1EntityId = request.getEnd1EntityId();
final Set<String> end2EntityTypeAndSuperTypes = request.getEnd2EntityTypeAndAllSuperTypes();
final Set<String> end2Classifications = new HashSet<>(request.getEnd2EntityClassifications());
final String end2EntityId = request.getEnd2EntityId();
String relationShipType = request.getRelationshipType();
RangerAccessResourceImpl rangerResource = new RangerAccessResourceImpl();
RangerAccessRequestImpl rangerRequest = new RangerAccessRequestImpl(rangerResource, action, request.getUser(), request.getUserGroups());
rangerRequest.setClientIPAddress(request.getClientIPAddress());
rangerRequest.setAccessTime(request.getAccessTime());
rangerRequest.setAction(action);
rangerRequest.setForwardedAddresses(request.getForwardedAddresses());
rangerRequest.setRemoteIPAddress(request.getRemoteIPAddress());
rangerResource.setValue(RESOURCE_RELATIONSHIP_TYPE, relationShipType);
Set<String> classificationsWithSuperTypesEnd1 = new HashSet();
for (String classificationToAuthorize : end1Classifications) {
classificationsWithSuperTypesEnd1.addAll(request.getClassificationTypeAndAllSuperTypes(classificationToAuthorize));
}
rangerResource.setValue(RESOURCE_END_ONE_ENTITY_TYPE, end1EntityTypeAndSuperTypes);
rangerResource.setValue(RESOURCE_END_ONE_ENTITY_CLASSIFICATION, classificationsWithSuperTypesEnd1);
rangerResource.setValue(RESOURCE_END_ONE_ENTITY_ID, end1EntityId);
Set<String> classificationsWithSuperTypesEnd2 = new HashSet();
for (String classificationToAuthorize : end2Classifications) {
classificationsWithSuperTypesEnd2.addAll(request.getClassificationTypeAndAllSuperTypes(classificationToAuthorize));
}
rangerResource.setValue(RESOURCE_END_TWO_ENTITY_TYPE, end2EntityTypeAndSuperTypes);
rangerResource.setValue(RESOURCE_END_TWO_ENTITY_CLASSIFICATION, classificationsWithSuperTypesEnd2);
rangerResource.setValue(RESOURCE_END_TWO_ENTITY_ID, end2EntityId);
ret = checkAccess(rangerRequest);
} finally {
RangerPerfTracer.log(perf);
}
if (LOG.isDebugEnabled()) {
LOG.debug("<== isAccessAllowed(" + request + "): " + ret);
}
return ret;
}
@Override
public void scrubSearchResults(AtlasSearchResultScrubRequest request) throws AtlasAuthorizationException {
if (LOG.isDebugEnabled()) {
LOG.debug("==> scrubSearchResults(" + request + ")");
}
RangerPerfTracer perf = null;
try {
if (RangerPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = RangerPerfTracer.getPerfTracer(PERF_LOG, "RangerAtlasAuthorizer.scrubSearchResults(" + request + ")");
}
final AtlasSearchResult result = request.getSearchResult();
if (CollectionUtils.isNotEmpty(result.getEntities())) {
for (AtlasEntityHeader entity : result.getEntities()) {
checkAccessAndScrub(entity, request);
}
}
if (CollectionUtils.isNotEmpty(result.getFullTextResult())) {
for (AtlasSearchResult.AtlasFullTextResult fullTextResult : result.getFullTextResult()) {
if (fullTextResult != null) {
checkAccessAndScrub(fullTextResult.getEntity(), request);
}
}
}
if (MapUtils.isNotEmpty(result.getReferredEntities())) {
for (AtlasEntityHeader entity : result.getReferredEntities().values()) {
checkAccessAndScrub(entity, request);
}
}
} finally {
RangerPerfTracer.log(perf);
}
if (LOG.isDebugEnabled()) {
LOG.debug("<== scrubSearchResults(): " + request);
}
}
private RangerServiceDef getServiceDef() {
RangerBasePlugin plugin = atlasPlugin;
return plugin != null ? plugin.getServiceDef() : null;
}
private boolean isAccessAllowed(AtlasEntityAccessRequest request, RangerAtlasAuditHandler auditHandler) throws AtlasAuthorizationException {
if (LOG.isDebugEnabled()) {
LOG.debug("==> isAccessAllowed(" + request + ")");
}
boolean ret = true;
try {
final String action = request.getAction() != null ? request.getAction().getType() : null;
final Set<String> entityTypes = request.getEntityTypeAndAllSuperTypes();
final String entityId = request.getEntityId();
final String classification = request.getClassification() != null ? request.getClassification().getTypeName() : null;
final RangerAccessRequestImpl rangerRequest = new RangerAccessRequestImpl();
final RangerAccessResourceImpl rangerResource = new RangerAccessResourceImpl();
final String ownerUser = request.getEntity() != null ? (String) request.getEntity().getAttribute(RESOURCE_ENTITY_OWNER) : null;
rangerResource.setValue(RESOURCE_ENTITY_TYPE, entityTypes);
rangerResource.setValue(RESOURCE_ENTITY_ID, entityId);
rangerResource.setOwnerUser(ownerUser);
rangerRequest.setAccessType(action);
rangerRequest.setAction(action);
rangerRequest.setUser(request.getUser());
rangerRequest.setUserGroups(request.getUserGroups());
rangerRequest.setClientIPAddress(request.getClientIPAddress());
rangerRequest.setAccessTime(request.getAccessTime());
rangerRequest.setResource(rangerResource);
rangerRequest.setForwardedAddresses(request.getForwardedAddresses());
rangerRequest.setRemoteIPAddress(request.getRemoteIPAddress());
if (StringUtils.isNotEmpty(classification)) {
rangerResource.setValue(RESOURCE_ENTITY_CLASSIFICATION, request.getClassificationTypeAndAllSuperTypes(classification));
ret = checkAccess(rangerRequest, auditHandler);
}
if (ret) {
if (CollectionUtils.isNotEmpty(request.getEntityClassifications())) {
// check authorization for each classification
for (String classificationToAuthorize : request.getEntityClassifications()) {
rangerResource.setValue(RESOURCE_ENTITY_CLASSIFICATION, request.getClassificationTypeAndAllSuperTypes(classificationToAuthorize));
ret = checkAccess(rangerRequest, auditHandler);
if (!ret) {
break;
}
}
} else {
rangerResource.setValue(RESOURCE_ENTITY_CLASSIFICATION, ENTITY_NOT_CLASSIFIED);
ret = checkAccess(rangerRequest, auditHandler);
}
}
} finally {
if(auditHandler != null) {
auditHandler.flushAudit();
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("<== isAccessAllowed(" + request + "): " + ret);
}
return ret;
}
private boolean checkAccess(RangerAccessRequestImpl request) {
boolean ret = false;
RangerBasePlugin plugin = atlasPlugin;
if (plugin != null) {
RangerAccessResult result = plugin.isAccessAllowed(request);
ret = result != null && result.getIsAllowed();
} else {
LOG.warn("RangerAtlasPlugin not initialized. Access blocked!!!");
}
return ret;
}
private boolean checkAccess(RangerAccessRequestImpl request, RangerAtlasAuditHandler auditHandler) {
boolean ret = false;
RangerBasePlugin plugin = atlasPlugin;
if (plugin != null) {
RangerAccessResult result = plugin.isAccessAllowed(request, auditHandler);
ret = result != null && result.getIsAllowed();
} else {
LOG.warn("RangerAtlasPlugin not initialized. Access blocked!!!");
}
return ret;
}
private void checkAccessAndScrub(AtlasEntityHeader entity, AtlasSearchResultScrubRequest request) throws AtlasAuthorizationException {
if (entity != null && request != null) {
final AtlasEntityAccessRequest entityAccessRequest = new AtlasEntityAccessRequest(request.getTypeRegistry(), AtlasPrivilege.ENTITY_READ, entity, request.getUser(), request.getUserGroups());
entityAccessRequest.setClientIPAddress(request.getClientIPAddress());
entityAccessRequest.setForwardedAddresses(request.getForwardedAddresses());
entityAccessRequest.setRemoteIPAddress(request.getRemoteIPAddress());
if (!isAccessAllowed(entityAccessRequest, null)) {
scrubEntityHeader(entity);
}
}
}
class RangerAtlasPlugin extends RangerBasePlugin {
RangerAtlasPlugin() {
super("atlas", "atlas");
}
}
class RangerAtlasAuditHandler extends RangerDefaultAuditHandler {
private final Map<String, AuthzAuditEvent> auditEvents;
private final String resourcePath;
private boolean denyExists = false;
public RangerAtlasAuditHandler(AtlasEntityAccessRequest request, RangerServiceDef serviceDef) {
Collection<String> classifications = request.getEntityClassifications();
String strClassifications = classifications == null ? "[]" : classifications.toString();
if (request.getClassification() != null) {
strClassifications += ("," + request.getClassification().getTypeName());
}
RangerAccessResourceImpl rangerResource = new RangerAccessResourceImpl();
rangerResource.setServiceDef(serviceDef);
rangerResource.setValue(RESOURCE_ENTITY_TYPE, request.getEntityType());
rangerResource.setValue(RESOURCE_ENTITY_CLASSIFICATION, strClassifications);
rangerResource.setValue(RESOURCE_ENTITY_ID, request.getEntityId());
auditEvents = new HashMap<>();
resourcePath = rangerResource.getAsString();
}
@Override
public void processResult(RangerAccessResult result) {
if (denyExists) { // nothing more to do, if a deny already encountered
return;
}
AuthzAuditEvent auditEvent = super.getAuthzEvents(result);
if (auditEvent != null) {
// audit event might have list of entity-types and classification-types; overwrite with the values in original request
if (resourcePath != null) {
auditEvent.setResourcePath(resourcePath);
}
if (!result.getIsAllowed()) {
denyExists = true;
auditEvents.clear();
}
auditEvents.put(auditEvent.getPolicyId() + auditEvent.getAccessType(), auditEvent);
}
}
public void flushAudit() {
if (auditEvents != null) {
for (AuthzAuditEvent auditEvent : auditEvents.values()) {
logAuthzAudit(auditEvent);
}
}
}
}
}