Repository: atlas Updated Branches: refs/heads/master 80f556a70 -> eb895a62f
ATLAS-2765: updated authorization model to scrub search-results (clear entity-attributes, classifications) for entities the user doesn't have read access to Project: http://git-wip-us.apache.org/repos/asf/atlas/repo Commit: http://git-wip-us.apache.org/repos/asf/atlas/commit/eb895a62 Tree: http://git-wip-us.apache.org/repos/asf/atlas/tree/eb895a62 Diff: http://git-wip-us.apache.org/repos/asf/atlas/diff/eb895a62 Branch: refs/heads/master Commit: eb895a62fde263ecf84da3a476594ca75a23022b Parents: 80f556a Author: Madhan Neethiraj <mad...@apache.org> Authored: Fri Jun 22 21:32:18 2018 -0700 Committer: Madhan Neethiraj <mad...@apache.org> Committed: Sat Jun 23 21:02:09 2018 -0700 ---------------------------------------------------------------------- .../authorize/AtlasAuthorizationUtils.java | 17 ++++++ .../apache/atlas/authorize/AtlasAuthorizer.java | 38 ++++++++++++++ .../atlas/authorize/AtlasNoneAuthorizer.java | 4 ++ .../AtlasSearchResultScrubRequest.java | 54 ++++++++++++++++++++ .../authorize/simple/AtlasSimpleAuthorizer.java | 50 ++++++++++++++++++ .../atlas/discovery/EntityDiscoveryService.java | 16 ++++++ .../apache/atlas/glossary/GlossaryService.java | 6 ++- .../store/graph/v2/AtlasEntityStoreV2.java | 4 +- 8 files changed, 186 insertions(+), 3 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/atlas/blob/eb895a62/authorization/src/main/java/org/apache/atlas/authorize/AtlasAuthorizationUtils.java ---------------------------------------------------------------------- diff --git a/authorization/src/main/java/org/apache/atlas/authorize/AtlasAuthorizationUtils.java b/authorization/src/main/java/org/apache/atlas/authorize/AtlasAuthorizationUtils.java index cc5db2f..a0d78eb 100644 --- a/authorization/src/main/java/org/apache/atlas/authorize/AtlasAuthorizationUtils.java +++ b/authorization/src/main/java/org/apache/atlas/authorize/AtlasAuthorizationUtils.java @@ -62,6 +62,23 @@ public class AtlasAuthorizationUtils { } } + public static void scrubSearchResults(AtlasSearchResultScrubRequest request) throws AtlasBaseException { + String userName = getCurrentUserName(); + + if (StringUtils.isNotEmpty(userName)) { + try { + AtlasAuthorizer authorizer = AtlasAuthorizerFactory.getAtlasAuthorizer(); + + request.setUser(userName, getCurrentUserGroups()); + request.setClientIPAddress(RequestContext.get().getClientIPAddress()); + + authorizer.scrubSearchResults(request); + } catch (AtlasAuthorizationException e) { + LOG.error("Unable to obtain AtlasAuthorizer", e); + } + } + } + public static boolean isAccessAllowed(AtlasAdminAccessRequest request) { boolean ret = false; String userName = getCurrentUserName(); http://git-wip-us.apache.org/repos/asf/atlas/blob/eb895a62/authorization/src/main/java/org/apache/atlas/authorize/AtlasAuthorizer.java ---------------------------------------------------------------------- diff --git a/authorization/src/main/java/org/apache/atlas/authorize/AtlasAuthorizer.java b/authorization/src/main/java/org/apache/atlas/authorize/AtlasAuthorizer.java index 285e0f6..563485e 100644 --- a/authorization/src/main/java/org/apache/atlas/authorize/AtlasAuthorizer.java +++ b/authorization/src/main/java/org/apache/atlas/authorize/AtlasAuthorizer.java @@ -19,6 +19,8 @@ package org.apache.atlas.authorize; +import org.apache.atlas.model.instance.AtlasEntityHeader; + public interface AtlasAuthorizer { /** * initialization of authorizer implementation @@ -55,4 +57,40 @@ public interface AtlasAuthorizer { * @throws AtlasAuthorizationException */ boolean isAccessAllowed(AtlasTypeAccessRequest request) throws AtlasAuthorizationException; + + + /** + * scrub search-results to handle entities for which the user doesn't have access + * @param request + * @return + * @throws AtlasAuthorizationException + */ + default + void scrubSearchResults(AtlasSearchResultScrubRequest request) throws AtlasAuthorizationException { + } + + default + void scrubEntityHeader(AtlasEntityHeader entity) { + entity.setGuid("-1"); + + if(entity.getAttributes() != null) { + entity.getAttributes().clear(); + } + + if(entity.getClassifications() != null) { + entity.getClassifications().clear(); + } + + if(entity.getClassificationNames() != null) { + entity.getClassificationNames().clear(); + } + + if(entity.getMeanings() != null) { + entity.getMeanings().clear(); + } + + if(entity.getMeaningNames() != null) { + entity.getMeaningNames().clear(); + } + } } http://git-wip-us.apache.org/repos/asf/atlas/blob/eb895a62/authorization/src/main/java/org/apache/atlas/authorize/AtlasNoneAuthorizer.java ---------------------------------------------------------------------- diff --git a/authorization/src/main/java/org/apache/atlas/authorize/AtlasNoneAuthorizer.java b/authorization/src/main/java/org/apache/atlas/authorize/AtlasNoneAuthorizer.java index 06ae600..9f8f7ac 100644 --- a/authorization/src/main/java/org/apache/atlas/authorize/AtlasNoneAuthorizer.java +++ b/authorization/src/main/java/org/apache/atlas/authorize/AtlasNoneAuthorizer.java @@ -44,4 +44,8 @@ public class AtlasNoneAuthorizer implements AtlasAuthorizer { public boolean isAccessAllowed(AtlasTypeAccessRequest request) throws AtlasAuthorizationException { return true; } + + public void scrubSearchResults(AtlasSearchResultScrubRequest request) throws AtlasAuthorizationException { + + } } http://git-wip-us.apache.org/repos/asf/atlas/blob/eb895a62/authorization/src/main/java/org/apache/atlas/authorize/AtlasSearchResultScrubRequest.java ---------------------------------------------------------------------- diff --git a/authorization/src/main/java/org/apache/atlas/authorize/AtlasSearchResultScrubRequest.java b/authorization/src/main/java/org/apache/atlas/authorize/AtlasSearchResultScrubRequest.java new file mode 100644 index 0000000..c908b28 --- /dev/null +++ b/authorization/src/main/java/org/apache/atlas/authorize/AtlasSearchResultScrubRequest.java @@ -0,0 +1,54 @@ +/** + * 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; + +import org.apache.atlas.model.discovery.AtlasSearchResult; +import org.apache.atlas.type.AtlasTypeRegistry; + +import java.util.Set; + +public class AtlasSearchResultScrubRequest extends AtlasAccessRequest { + private final AtlasTypeRegistry typeRegistry; + private final AtlasSearchResult searchResult; + + + public AtlasSearchResultScrubRequest(AtlasTypeRegistry typeRegistry, AtlasSearchResult searchResult) { + this(typeRegistry, searchResult, null, null); + } + + public AtlasSearchResultScrubRequest(AtlasTypeRegistry typeRegistry, AtlasSearchResult searchResult, String userName, Set<String> userGroups) { + super(AtlasPrivilege.ENTITY_READ, userName, userGroups); + + this.typeRegistry = typeRegistry; + this.searchResult = searchResult; + } + + public AtlasTypeRegistry getTypeRegistry() { return typeRegistry; } + + public AtlasSearchResult getSearchResult() { + return searchResult; + } + + @Override + public String toString() { + return "AtlasSearchResultScrubRequest[searchResult=" + searchResult + ", action=" + getAction() + ", accessTime=" + getAccessTime() + ", user=" + getUser() + + ", userGroups=" + getUserGroups() + ", clientIPAddress=" + getClientIPAddress() + "]"; + } +} + + http://git-wip-us.apache.org/repos/asf/atlas/blob/eb895a62/authorization/src/main/java/org/apache/atlas/authorize/simple/AtlasSimpleAuthorizer.java ---------------------------------------------------------------------- diff --git a/authorization/src/main/java/org/apache/atlas/authorize/simple/AtlasSimpleAuthorizer.java b/authorization/src/main/java/org/apache/atlas/authorize/simple/AtlasSimpleAuthorizer.java index fd58858..47c44cc 100644 --- a/authorization/src/main/java/org/apache/atlas/authorize/simple/AtlasSimpleAuthorizer.java +++ b/authorization/src/main/java/org/apache/atlas/authorize/simple/AtlasSimpleAuthorizer.java @@ -31,10 +31,16 @@ import org.apache.atlas.authorize.AtlasAdminAccessRequest; import org.apache.atlas.authorize.AtlasAuthorizer; import org.apache.atlas.authorize.AtlasAuthorizationException; import org.apache.atlas.authorize.AtlasEntityAccessRequest; +import org.apache.atlas.authorize.AtlasPrivilege; +import org.apache.atlas.authorize.AtlasSearchResultScrubRequest; import org.apache.atlas.authorize.AtlasTypeAccessRequest; import org.apache.atlas.authorize.simple.AtlasSimpleAuthzPolicy.*; +import org.apache.atlas.model.discovery.AtlasSearchResult; +import org.apache.atlas.model.discovery.AtlasSearchResult.AtlasFullTextResult; +import org.apache.atlas.model.instance.AtlasEntityHeader; import org.apache.atlas.utils.AtlasJson; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -219,6 +225,38 @@ public final class AtlasSimpleAuthorizer implements AtlasAuthorizer { return ret; } + @Override + public void scrubSearchResults(AtlasSearchResultScrubRequest request) throws AtlasAuthorizationException { + if (LOG.isDebugEnabled()) { + LOG.debug("==> SimpleAtlasAuthorizer.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 (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); + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== SimpleAtlasAuthorizer.scrubSearchResults({}): {}", request, result); + } + } private Set<String> getRoles(String userName, Set<String> userGroups) { Set<String> ret = new HashSet<>(); @@ -341,6 +379,18 @@ public final class AtlasSimpleAuthorizer implements AtlasAuthorizer { 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()); + + if (!isAccessAllowed(entityAccessRequest)) { + scrubEntityHeader(entity); + } + } + } } http://git-wip-us.apache.org/repos/asf/atlas/blob/eb895a62/repository/src/main/java/org/apache/atlas/discovery/EntityDiscoveryService.java ---------------------------------------------------------------------- diff --git a/repository/src/main/java/org/apache/atlas/discovery/EntityDiscoveryService.java b/repository/src/main/java/org/apache/atlas/discovery/EntityDiscoveryService.java index 9d0cc9d..2378d6b 100644 --- a/repository/src/main/java/org/apache/atlas/discovery/EntityDiscoveryService.java +++ b/repository/src/main/java/org/apache/atlas/discovery/EntityDiscoveryService.java @@ -23,6 +23,8 @@ import org.apache.atlas.AtlasErrorCode; import org.apache.atlas.AtlasException; import org.apache.atlas.SortOrder; import org.apache.atlas.annotation.GraphTransaction; +import org.apache.atlas.authorize.AtlasAuthorizationUtils; +import org.apache.atlas.authorize.AtlasSearchResultScrubRequest; import org.apache.atlas.exception.AtlasBaseException; import org.apache.atlas.model.discovery.AtlasSearchResult; import org.apache.atlas.model.discovery.AtlasSearchResult.AtlasFullTextResult; @@ -167,6 +169,8 @@ public class EntityDiscoveryService implements AtlasDiscoveryService { } } + scrubSearchResults(ret); + return ret; } @@ -183,6 +187,8 @@ public class EntityDiscoveryService implements AtlasDiscoveryService { } ret.setFullTextResult(getIndexQueryResults(idxQuery, params, excludeDeletedEntities)); + scrubSearchResults(ret); + return ret; } @@ -408,6 +414,8 @@ public class EntityDiscoveryService implements AtlasDiscoveryService { } } + scrubSearchResults(ret); + return ret; } @@ -504,6 +512,8 @@ public class EntityDiscoveryService implements AtlasDiscoveryService { searchTracker.remove(searchID); } + scrubSearchResults(ret); + return ret; } @@ -612,6 +622,8 @@ public class EntityDiscoveryService implements AtlasDiscoveryService { graph.releaseGremlinScriptEngine(scriptEngine); } + scrubSearchResults(ret); + return ret; } @@ -929,4 +941,8 @@ public class EntityDiscoveryService implements AtlasDiscoveryService { throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, "invalid data"); } } + + private void scrubSearchResults(AtlasSearchResult result) throws AtlasBaseException { + AtlasAuthorizationUtils.scrubSearchResults(new AtlasSearchResultScrubRequest(typeRegistry, result)); + } } http://git-wip-us.apache.org/repos/asf/atlas/blob/eb895a62/repository/src/main/java/org/apache/atlas/glossary/GlossaryService.java ---------------------------------------------------------------------- diff --git a/repository/src/main/java/org/apache/atlas/glossary/GlossaryService.java b/repository/src/main/java/org/apache/atlas/glossary/GlossaryService.java index fb26598..9229d2d 100644 --- a/repository/src/main/java/org/apache/atlas/glossary/GlossaryService.java +++ b/repository/src/main/java/org/apache/atlas/glossary/GlossaryService.java @@ -1026,13 +1026,17 @@ public class GlossaryService { Map<String, AtlasGlossaryTerm> termMap = new HashMap<>(); dataAccess.load(terms).iterator().forEachRemaining(t -> termMap.put(t.getGuid(), t)); - termHeaders.forEach(t -> t.setDisplayText(termMap.get(t.getTermGuid()).getName())); + termHeaders.forEach(t -> t.setDisplayText(getDisplayText(termMap.get(t.getTermGuid())))); } private boolean isNameInvalid(String name) { return StringUtils.containsAny(name, invalidNameChars); } + private String getDisplayText(AtlasGlossaryTerm term) { + return term != null ? term.getName() : null; + } + static class PaginationHelper<T> { private int pageStart; private int pageEnd; http://git-wip-us.apache.org/repos/asf/atlas/blob/eb895a62/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasEntityStoreV2.java ---------------------------------------------------------------------- diff --git a/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasEntityStoreV2.java b/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasEntityStoreV2.java index 40593be..1b709e4 100644 --- a/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasEntityStoreV2.java +++ b/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasEntityStoreV2.java @@ -525,7 +525,7 @@ public class AtlasEntityStoreV2 implements AtlasEntityStore { AtlasEntityHeader entityHeader = entityRetriever.toAtlasEntityHeaderWithClassifications(guid); - AtlasAuthorizationUtils.verifyAccess(new AtlasEntityAccessRequest(typeRegistry, AtlasPrivilege.ENTITY_READ_CLASSIFICATION, entityHeader), "get classifications: guid=", guid); + AtlasAuthorizationUtils.verifyAccess(new AtlasEntityAccessRequest(typeRegistry, AtlasPrivilege.ENTITY_READ, entityHeader), "get classifications: guid=", guid); return entityHeader.getClassifications(); } @@ -541,7 +541,7 @@ public class AtlasEntityStoreV2 implements AtlasEntityStore { AtlasEntityHeader entityHeader = entityRetriever.toAtlasEntityHeaderWithClassifications(guid); if (CollectionUtils.isNotEmpty(entityHeader.getClassifications())) { - AtlasAuthorizationUtils.verifyAccess(new AtlasEntityAccessRequest(typeRegistry, AtlasPrivilege.ENTITY_READ_CLASSIFICATION, entityHeader), "get classification: guid=", guid, ", classification=", classificationName); + AtlasAuthorizationUtils.verifyAccess(new AtlasEntityAccessRequest(typeRegistry, AtlasPrivilege.ENTITY_READ, entityHeader), "get classification: guid=", guid, ", classification=", classificationName); for (AtlasClassification classification : entityHeader.getClassifications()) { if (!StringUtils.equalsIgnoreCase(classification.getTypeName(), classificationName)) {