NIFI-12696 Added Component Type to Rule Violations for Authorization

RuleViolations (these objects only reside in memory only) now contain the type of the component that is responsible for the violation. This is used in StandardNiFiServiceFacade to fix and improve the authorization logic.

This closes #8318

Signed-off-by: David Handermann <exceptionfactory@apache.org>
This commit is contained in:
tpalfy 2024-01-30 18:00:08 +01:00 committed by exceptionfactory
parent 5f534dcc42
commit 49e599385d
No known key found for this signature in database
4 changed files with 52 additions and 34 deletions

View File

@ -18,6 +18,7 @@ package org.apache.nifi.validation;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.nifi.flow.ComponentType;
import org.apache.nifi.flowanalysis.EnforcementPolicy;
import java.util.StringJoiner;
@ -32,6 +33,7 @@ public class RuleViolation {
private final String scope;
private final String subjectId;
private final String subjectDisplayName;
private final ComponentType subjectComponentType;
private final String groupId;
private final String ruleId;
private final String issueId;
@ -45,6 +47,7 @@ public class RuleViolation {
String scope,
String subjectId,
String subjectDisplayName,
ComponentType subjectComponentType,
String groupId,
String ruleId,
String issueId,
@ -55,6 +58,7 @@ public class RuleViolation {
this.scope = scope;
this.subjectId = subjectId;
this.subjectDisplayName = subjectDisplayName;
this.subjectComponentType = subjectComponentType;
this.groupId = groupId;
this.ruleId = ruleId;
this.issueId = issueId;
@ -92,6 +96,13 @@ public class RuleViolation {
return subjectDisplayName;
}
/**
* @return the type of the subject that violated the rule
*/
public ComponentType getSubjectComponentType() {
return subjectComponentType;
}
/**
* @return group id - if this violation is a result of a component analysis, then the id of the group of the component.
* If this violation is a result of a group analysis, then the id of that group itself.
@ -146,6 +157,7 @@ public class RuleViolation {
.add("scope='" + scope + "'")
.add("subjectId='" + subjectId + "'")
.add("subjectDisplayName='" + subjectDisplayName + "'")
.add("subjectComponentType='" + subjectComponentType + "'")
.add("groupId='" + groupId + "'")
.add("issueId='" + issueId + "'")
.add("ruleId='" + ruleId + "'")

View File

@ -23,6 +23,7 @@ import org.apache.nifi.controller.flowanalysis.FlowAnalysisUtil;
import org.apache.nifi.controller.flowanalysis.FlowAnalyzer;
import org.apache.nifi.controller.service.ControllerServiceNode;
import org.apache.nifi.controller.service.ControllerServiceProvider;
import org.apache.nifi.flow.ComponentType;
import org.apache.nifi.flow.VersionedComponent;
import org.apache.nifi.flow.VersionedConnection;
import org.apache.nifi.flow.VersionedControllerService;
@ -108,6 +109,8 @@ public class StandardFlowAnalyzer implements FlowAnalyzer {
long start = System.currentTimeMillis();
String componentId = component.getIdentifier();
ComponentType componentType = component.getComponentType();
Set<FlowAnalysisRuleNode> flowAnalysisRules = flowAnalysisRuleProvider.getAllFlowAnalysisRules();
Set<RuleViolation> violations = flowAnalysisRules.stream()
@ -126,6 +129,7 @@ public class StandardFlowAnalyzer implements FlowAnalyzer {
componentId,
componentId,
getDisplayName(component),
componentType,
component.getGroupIdentifier(),
ruleId,
analysisResult.getIssueId(),
@ -175,6 +179,7 @@ public class StandardFlowAnalyzer implements FlowAnalyzer {
Map<VersionedComponent, Collection<RuleViolation>> componentToRuleViolations
) {
String groupId = processGroup.getIdentifier();
ComponentType processGroupComponentType = processGroup.getComponentType();
flowAnalysisRules.stream()
.filter(FlowAnalysisRuleNode::isEnabled)
@ -199,6 +204,7 @@ public class StandardFlowAnalyzer implements FlowAnalyzer {
component.getGroupIdentifier(),
component.getIdentifier(),
getDisplayName(component),
component.getComponentType(),
component.getGroupIdentifier(),
ruleId,
analysisResult.getIssueId(),
@ -212,6 +218,7 @@ public class StandardFlowAnalyzer implements FlowAnalyzer {
groupId,
groupId,
getDisplayName(processGroup),
processGroupComponentType,
groupId,
ruleId,
analysisResult.getIssueId(),

View File

@ -27,7 +27,6 @@ import org.apache.nifi.admin.service.AuditService;
import org.apache.nifi.attribute.expression.language.Query;
import org.apache.nifi.authorization.AccessDeniedException;
import org.apache.nifi.authorization.AccessPolicy;
import org.apache.nifi.authorization.AuthorizableHolder;
import org.apache.nifi.authorization.AuthorizableLookup;
import org.apache.nifi.authorization.AuthorizationRequest;
import org.apache.nifi.authorization.AuthorizationResult;
@ -6486,10 +6485,16 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
ruleViolationDto.setViolationMessage(ruleViolation.getViolationMessage());
String subjectId = ruleViolation.getSubjectId();
String groupId = ruleViolation.getGroupId();
ruleViolationDto.setSubjectId(subjectId);
ruleViolationDto.setGroupId(ruleViolation.getGroupId());
ruleViolationDto.setGroupId(groupId);
ruleViolationDto.setSubjectDisplayName(ruleViolation.getSubjectDisplayName());
ruleViolationDto.setSubjectPermissionDto(createPermissionDto(subjectId));
ruleViolationDto.setSubjectPermissionDto(createPermissionDto(
subjectId,
ruleViolation.getSubjectComponentType(),
groupId
));
return ruleViolationDto;
})
@ -6501,21 +6506,33 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
return entity;
}
private PermissionsDTO createPermissionDto(String id) {
private PermissionsDTO createPermissionDto(
final String id,
final org.apache.nifi.flow.ComponentType subjectComponentType,
final String groupId
) {
final InstantiatedVersionedComponent versionedComponent = new InstantiatedVersionedComponent() {
@Override
public String getInstanceIdentifier() {
return id;
}
Optional<AuthorizableHolder> authorizableHolder = findAuthorizableHolder(
id,
authorizableLookup::getProcessor,
authorizableLookup::getControllerService,
authorizableLookup::getConnection,
authorizableLookup::getProcessGroup,
authorizableLookup::getPublicInputPort,
authorizableLookup::getPublicOutputPort
);
@Override
public String getInstanceGroupId() {
return groupId;
}
};
Authorizable authorizable;
try {
authorizable = getAuthorizable(subjectComponentType.name(), versionedComponent);
} catch (Exception e) {
authorizable = null;
}
final PermissionsDTO permissionDto;
if (authorizableHolder.isPresent()) {
permissionDto = dtoFactory.createPermissionsDto(authorizableHolder.get().getAuthorizable(), NiFiUserUtils.getNiFiUser());
if (authorizable != null) {
permissionDto = dtoFactory.createPermissionsDto(authorizable, NiFiUserUtils.getNiFiUser());
} else {
permissionDto = new PermissionsDTO();
permissionDto.setCanRead(false);
@ -6525,25 +6542,6 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
return permissionDto;
}
private Optional<AuthorizableHolder> findAuthorizableHolder(
final String id,
final Function<String, AuthorizableHolder>... lookupMethods
) {
AuthorizableHolder authorizableHolder = null;
for (Function<String, AuthorizableHolder> lookupMethod : lookupMethods) {
try {
authorizableHolder = lookupMethod.apply(id);
} catch (ResourceNotFoundException e) {
// We don't know beforehand what kind of component we are looking for. Ignore if one lookup fails.
}
if (authorizableHolder != null) {
break;
}
}
return Optional.ofNullable(authorizableHolder);
}
/* reusable function declarations for converting ids to tenant entities */
private Function<String, TenantEntity> mapUserGroupIdToTenantEntity(final boolean enforceGroupExistence) {
return userGroupId -> {

View File

@ -926,6 +926,7 @@ public class StandardNiFiServiceFacadeTest {
"scope" + ruleViolationCounter,
"subjectId" + ruleViolationCounter,
"subjectDisplayName" + ruleViolationCounter,
null,
groupId,
"ruleId" + ruleViolationCounter,
"issueId" + ruleViolationCounter,