Skip to content

Commit

Permalink
Merge pull request #2243 from atlanhq/gov-1233
Browse files Browse the repository at this point in the history
[master] GOV-1233 Readme/Link CRUD Governance issue
  • Loading branch information
nikhilbonte21 authored Aug 10, 2023
2 parents eca1f8e + aefffe2 commit 36c9db8
Show file tree
Hide file tree
Showing 6 changed files with 315 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import org.apache.atlas.AtlasErrorCode;
import org.apache.atlas.RequestContext;
import org.apache.atlas.exception.AtlasBaseException;
import org.apache.atlas.model.instance.AtlasEntityHeader;
import org.apache.atlas.type.AtlasTypeRegistry;
import org.apache.atlas.utils.AtlasPerfMetrics.MetricRecorder;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
Expand All @@ -35,6 +37,9 @@
import java.net.UnknownHostException;
import java.util.*;

import static org.apache.atlas.repository.Constants.SKIP_DELETE_AUTH_CHECK_TYPES;
import static org.apache.atlas.repository.Constants.SKIP_UPDATE_AUTH_CHECK_TYPES;

public class AtlasAuthorizationUtils {
private static final Logger LOG = LoggerFactory.getLogger(AtlasAuthorizationUtils.class);

Expand All @@ -54,6 +59,20 @@ public static void verifyAccess(AtlasTypeAccessRequest request, Object... errorM
}
}

public static void verifyUpdateEntityAccess(AtlasTypeRegistry typeRegistry, AtlasEntityHeader entityHeader, String message) throws AtlasBaseException {
if (!SKIP_UPDATE_AUTH_CHECK_TYPES.contains(entityHeader.getTypeName())) {
AtlasEntityAccessRequest request = new AtlasEntityAccessRequest(typeRegistry, AtlasPrivilege.ENTITY_UPDATE, entityHeader);
verifyAccess(request, message);
}
}

public static void verifyDeleteEntityAccess(AtlasTypeRegistry typeRegistry, AtlasEntityHeader entityHeader, String message) throws AtlasBaseException {
if (!SKIP_DELETE_AUTH_CHECK_TYPES.contains(entityHeader.getTypeName())) {
AtlasEntityAccessRequest request = new AtlasEntityAccessRequest(typeRegistry, AtlasPrivilege.ENTITY_DELETE, entityHeader);
verifyAccess(request, message);
}
}

public static void verifyAccess(AtlasEntityAccessRequest request, Object... errorMsgParams) throws AtlasBaseException {
if (! isAccessAllowed(request)) {
String message = (errorMsgParams != null && errorMsgParams.length > 0) ? StringUtils.join(errorMsgParams) : "";
Expand Down
18 changes: 18 additions & 0 deletions common/src/main/java/org/apache/atlas/repository/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

import static org.apache.atlas.type.AtlasStructType.AtlasAttribute.encodePropertyKey;

Expand Down Expand Up @@ -154,6 +156,12 @@ public final class Constants {
* Resource
*/
public static final String LINK_ENTITY_TYPE = "Link";
public static final String README_ENTITY_TYPE = "Readme";

public static final String ASSET_RELATION_ATTR = "asset";

public static final String ASSET_README_EDGE_LABEL = "__Asset.readme";
public static final String ASSET_LINK_EDGE_LABEL = "__Asset.links";

/**
* Lineage relations.
Expand Down Expand Up @@ -398,6 +406,16 @@ public enum SupportedFileExtensions { XLSX, XLS, CSV }
public static final String REQUEST_HEADER_USER_AGENT = "User-Agent";
public static final String REQUEST_HEADER_HOST = "Host";

public static final Set<String> SKIP_UPDATE_AUTH_CHECK_TYPES = new HashSet<String>() {{
add(README_ENTITY_TYPE);
add(LINK_ENTITY_TYPE);
}};

public static final Set<String> SKIP_DELETE_AUTH_CHECK_TYPES = new HashSet<String>() {{
add(README_ENTITY_TYPE);
add(LINK_ENTITY_TYPE);
}};

private Constants() {
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,14 @@
import org.apache.atlas.repository.store.graph.v1.RestoreHandlerV1;
import org.apache.atlas.repository.store.graph.v2.preprocessor.AuthPolicyPreProcessor;
import org.apache.atlas.repository.store.graph.v2.preprocessor.ConnectionPreProcessor;
import org.apache.atlas.repository.store.graph.v2.preprocessor.LinkPreProcessor;
import org.apache.atlas.repository.store.graph.v2.preprocessor.resource.LinkPreProcessor;
import org.apache.atlas.repository.store.graph.v2.preprocessor.PreProcessor;
import org.apache.atlas.repository.store.graph.v2.preprocessor.accesscontrol.PersonaPreProcessor;
import org.apache.atlas.repository.store.graph.v2.preprocessor.accesscontrol.PurposePreProcessor;
import org.apache.atlas.repository.store.graph.v2.preprocessor.glossary.CategoryPreProcessor;
import org.apache.atlas.repository.store.graph.v2.preprocessor.glossary.GlossaryPreProcessor;
import org.apache.atlas.repository.store.graph.v2.preprocessor.glossary.TermPreProcessor;
import org.apache.atlas.repository.store.graph.v2.preprocessor.resource.ReadmePreProcessor;
import org.apache.atlas.repository.store.graph.v2.preprocessor.sql.QueryCollectionPreProcessor;
import org.apache.atlas.repository.store.graph.v2.preprocessor.sql.QueryFolderPreProcessor;
import org.apache.atlas.repository.store.graph.v2.preprocessor.sql.QueryPreProcessor;
Expand Down Expand Up @@ -509,7 +510,7 @@ public EntityMutationResponse updateByUniqueAttributes(AtlasEntityType entityTyp

entity.setGuid(guid);

AtlasAuthorizationUtils.verifyAccess(new AtlasEntityAccessRequest(typeRegistry, AtlasPrivilege.ENTITY_UPDATE, new AtlasEntityHeader(entity)), "update entity ByUniqueAttributes");
AtlasAuthorizationUtils.verifyUpdateEntityAccess(typeRegistry, new AtlasEntityHeader(entity), "update entity ByUniqueAttributes");

return createOrUpdate(new AtlasEntityStream(updatedEntityInfo), true, false, false, false);
}
Expand All @@ -526,7 +527,7 @@ public EntityMutationResponse updateEntityAttributeByGuid(String guid, String at
AtlasEntityType entityType = (AtlasEntityType) typeRegistry.getType(entity.getTypeName());
AtlasAttribute attr = entityType.getAttribute(attrName);

AtlasAuthorizationUtils.verifyAccess(new AtlasEntityAccessRequest(typeRegistry, AtlasPrivilege.ENTITY_UPDATE, entity), "update entity ByUniqueAttributes : guid=", guid );
AtlasAuthorizationUtils.verifyUpdateEntityAccess(typeRegistry, entity, "update entity ByUniqueAttributes : guid=" + guid);

if (attr == null) {
attr = entityType.getRelationshipAttribute(attrName, AtlasEntityUtil.getRelationshipType(attrValue));
Expand Down Expand Up @@ -581,7 +582,7 @@ public EntityMutationResponse deleteById(final String guid) throws AtlasBaseExce
if (vertex != null) {
AtlasEntityHeader entityHeader = entityRetriever.toAtlasEntityHeaderWithClassifications(vertex);

AtlasAuthorizationUtils.verifyAccess(new AtlasEntityAccessRequest(typeRegistry, AtlasPrivilege.ENTITY_DELETE, entityHeader), "delete entity: guid=", guid);
AtlasAuthorizationUtils.verifyDeleteEntityAccess(typeRegistry, entityHeader, "delete entity: guid=" + guid);

deletionCandidates.add(vertex);
} else {
Expand Down Expand Up @@ -627,7 +628,7 @@ public EntityMutationResponse deleteByIds(final List<String> guids) throws Atlas

AtlasEntityHeader entityHeader = entityRetriever.toAtlasEntityHeaderWithClassifications(vertex);

AtlasAuthorizationUtils.verifyAccess(new AtlasEntityAccessRequest(typeRegistry, AtlasPrivilege.ENTITY_DELETE, entityHeader), "delete entity: guid=", guid);
AtlasAuthorizationUtils.verifyDeleteEntityAccess(typeRegistry, entityHeader, "delete entity: guid=" + guid);

deletionCandidates.add(vertex);
}
Expand Down Expand Up @@ -670,7 +671,7 @@ public EntityMutationResponse restoreByIds(final List<String> guids) throws Atla

AtlasEntityHeader entityHeader = entityRetriever.toAtlasEntityHeaderWithClassifications(vertex);

AtlasAuthorizationUtils.verifyAccess(new AtlasEntityAccessRequest(typeRegistry, AtlasPrivilege.ENTITY_DELETE, entityHeader), "delete entity: guid=", guid);
AtlasAuthorizationUtils.verifyDeleteEntityAccess(typeRegistry, entityHeader, "delete entity: guid=" + guid);

restoreCandidates.add(vertex);
}
Expand Down Expand Up @@ -736,7 +737,8 @@ public EntityMutationResponse deleteByUniqueAttributes(AtlasEntityType entityTyp
if (vertex != null) {
AtlasEntityHeader entityHeader = entityRetriever.toAtlasEntityHeaderWithClassifications(vertex);

AtlasAuthorizationUtils.verifyAccess(new AtlasEntityAccessRequest(typeRegistry, AtlasPrivilege.ENTITY_DELETE, entityHeader), "delete entity: typeName=", entityType.getTypeName(), ", uniqueAttributes=", uniqAttributes);
AtlasAuthorizationUtils.verifyDeleteEntityAccess(typeRegistry, entityHeader,
"delete entity: typeName=" + entityType.getTypeName() + ", uniqueAttributes=" + uniqAttributes);

deletionCandidates.add(vertex);
} else {
Expand Down Expand Up @@ -788,7 +790,8 @@ public EntityMutationResponse deleteByUniqueAttributes(List<AtlasObjectId> objec
if (vertex != null) {
AtlasEntityHeader entityHeader = entityRetriever.toAtlasEntityHeaderWithClassifications(vertex);

AtlasAuthorizationUtils.verifyAccess(new AtlasEntityAccessRequest(typeRegistry, AtlasPrivilege.ENTITY_DELETE, entityHeader), "delete entity: typeName=", entityType.getTypeName(), ", uniqueAttributes=", objectId.getUniqueAttributes());
AtlasAuthorizationUtils.verifyDeleteEntityAccess(typeRegistry, entityHeader,
"delete entity: typeName=" + entityType.getTypeName() + ", uniqueAttributes=" + objectId.getUniqueAttributes());

deletionCandidates.add(vertex);
} else {
Expand Down Expand Up @@ -1502,11 +1505,9 @@ private EntityMutationResponse createOrUpdate(EntityStream entityStream, boolean
boolean skipAuthMeaningsUpdate = diffEntity != null && MapUtils.isNotEmpty(diffEntity.getRelationshipAttributes()) && diffEntity.getRelationshipAttributes().containsKey("meanings") && diffEntity.getRelationshipAttributes().size() == 1 && MapUtils.isEmpty(diffEntity.getAttributes());
boolean skipAuthStarredDetailsUpdate = diffEntity != null && MapUtils.isEmpty(diffEntity.getRelationshipAttributes()) && MapUtils.isNotEmpty(diffEntity.getAttributes()) && diffEntity.getAttributes().size() == 3 && diffEntity.getAttributes().containsKey(ATTR_STARRED_BY) && diffEntity.getAttributes().containsKey(ATTR_STARRED_COUNT) && diffEntity.getAttributes().containsKey(ATTR_STARRED_DETAILS_LIST);
if (skipAuthBaseConditions && (skipAuthMeaningsUpdate || skipAuthStarredDetailsUpdate)) {
//do nothing, only diff is relationshipAttributes.meanings, allow update
}
else {
AtlasEntityAccessRequest accessRequest = new AtlasEntityAccessRequest(typeRegistry, AtlasPrivilege.ENTITY_UPDATE, entityHeader);
AtlasAuthorizationUtils.verifyAccess(accessRequest, "update entity: type=", entity.getTypeName());
//do nothing, only diff is relationshipAttributes.meanings or starred, allow update
} else {
AtlasAuthorizationUtils.verifyUpdateEntityAccess(typeRegistry, entityHeader,"update entity: type=" + entity.getTypeName());
}
}
}
Expand Down Expand Up @@ -1828,7 +1829,11 @@ public PreProcessor getPreProcessor(String typeName) {
break;

case LINK_ENTITY_TYPE:
preProcessor = new LinkPreProcessor();
preProcessor = new LinkPreProcessor(typeRegistry, entityRetriever);
break;

case README_ENTITY_TYPE:
preProcessor = new ReadmePreProcessor(typeRegistry, entityRetriever);
break;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/**
* 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.repository.store.graph.v2.preprocessor.resource;

import org.apache.atlas.RequestContext;
import org.apache.atlas.authorize.AtlasAuthorizationUtils;
import org.apache.atlas.authorize.AtlasEntityAccessRequest;
import org.apache.atlas.authorize.AtlasPrivilege;
import org.apache.atlas.exception.AtlasBaseException;
import org.apache.atlas.model.instance.AtlasEntity;
import org.apache.atlas.model.instance.AtlasEntityHeader;
import org.apache.atlas.model.instance.AtlasObjectId;
import org.apache.atlas.repository.graphdb.AtlasEdgeDirection;
import org.apache.atlas.repository.graphdb.AtlasVertex;
import org.apache.atlas.repository.store.graph.v2.EntityGraphRetriever;
import org.apache.atlas.repository.store.graph.v2.preprocessor.PreProcessor;
import org.apache.atlas.type.AtlasTypeRegistry;
import org.apache.atlas.utils.AtlasPerfMetrics;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Iterator;

import static org.apache.atlas.repository.Constants.ACTIVE_STATE_VALUE;
import static org.apache.atlas.repository.Constants.ASSET_RELATION_ATTR;
import static org.apache.atlas.repository.Constants.STATE_PROPERTY_KEY;

public abstract class AbstractResourcePreProcessor implements PreProcessor {
private static final Logger LOG = LoggerFactory.getLogger(AbstractResourcePreProcessor.class);

private final AtlasTypeRegistry typeRegistry;
private final EntityGraphRetriever entityRetriever;

AbstractResourcePreProcessor(AtlasTypeRegistry typeRegistry,
EntityGraphRetriever entityRetriever) {
this.typeRegistry = typeRegistry;
this.entityRetriever = entityRetriever;
}

void authorizeResourceUpdate(AtlasEntity resourceEntity, AtlasVertex ResourceVertex, String edgeLabel) throws AtlasBaseException {
AtlasPerfMetrics.MetricRecorder metricRecorder = RequestContext.get().startMetricRecord("authorizeResourceUpdate");

try {
AtlasEntityHeader assetEntity = null;

AtlasObjectId asset = getAssetRelationAttr(resourceEntity);
if (asset != null) {
//Found linked asset in payload
AtlasVertex assetVertex = entityRetriever.getEntityVertex(asset);
assetEntity = entityRetriever.toAtlasEntityHeader(assetVertex);

} else {
//Check for linked asset in store
Iterator atlasVertexIterator = ResourceVertex.query()
.direction(AtlasEdgeDirection.IN)
.label(edgeLabel)
.has(STATE_PROPERTY_KEY, ACTIVE_STATE_VALUE)
.vertices()
.iterator();

if (atlasVertexIterator.hasNext()) {
//Found linked asset in store
AtlasVertex assetVertex = (AtlasVertex) atlasVertexIterator.next();
assetEntity = entityRetriever.toAtlasEntityHeader(assetVertex);
}
}

if (assetEntity != null) {
//First authorize entity update access
verifyAssetAccess(assetEntity, AtlasPrivilege.ENTITY_UPDATE, resourceEntity, AtlasPrivilege.ENTITY_UPDATE);
} else {
//No linked asset to the Resource, check for resource update permission
verifyAccess(resourceEntity, AtlasPrivilege.ENTITY_UPDATE);
}

} finally {
RequestContext.get().endMetricRecord(metricRecorder);
}
}

void authorizeResourceDelete(AtlasVertex resourceVertex) throws AtlasBaseException {
AtlasPerfMetrics.MetricRecorder recorder = RequestContext.get().startMetricRecord("authorizeResourceDelete");

try {
AtlasEntity resourceEntity = entityRetriever.toAtlasEntity(resourceVertex);

AtlasObjectId asset = getAssetRelationAttr(resourceEntity);
if (asset != null) {
AtlasEntityHeader assetEntity = entityRetriever.toAtlasEntityHeader(asset.getGuid());
verifyAssetAccess(assetEntity, AtlasPrivilege.ENTITY_UPDATE, resourceEntity, AtlasPrivilege.ENTITY_DELETE);
} else {
//No linked asset to the Resource, check for resource delete permission
verifyAccess(resourceEntity, AtlasPrivilege.ENTITY_DELETE);
}
} finally {
RequestContext.get().endMetricRecord(recorder);
}
}

private AtlasObjectId getAssetRelationAttr(AtlasEntity entity) {
AtlasObjectId ret = null;

if (entity.hasRelationshipAttribute(ASSET_RELATION_ATTR) &&
entity.getRelationshipAttribute(ASSET_RELATION_ATTR) != null) {
ret = (AtlasObjectId) entity.getRelationshipAttribute(ASSET_RELATION_ATTR);
}

return ret;
}

private void verifyAssetAccess(AtlasEntityHeader asset, AtlasPrivilege assetPrivilege,
AtlasEntity resource, AtlasPrivilege resourcePrivilege) throws AtlasBaseException {
verifyAccess(asset, assetPrivilege);
verifyAccess(resource, resourcePrivilege);
}

private void verifyAccess(AtlasEntity entity, AtlasPrivilege privilege) throws AtlasBaseException {
verifyAccess(new AtlasEntityHeader(entity), privilege);
}

private void verifyAccess(AtlasEntityHeader entityHeader, AtlasPrivilege privilege) throws AtlasBaseException {
String errorMessage = privilege.name() + " entity: " + entityHeader.getTypeName();
AtlasAuthorizationUtils.verifyAccess(new AtlasEntityAccessRequest(typeRegistry, privilege, entityHeader), errorMessage);
}
}
Loading

0 comments on commit 36c9db8

Please sign in to comment.