Skip to content

Commit

Permalink
Implement library function access checks
Browse files Browse the repository at this point in the history
The profiles were there, this commit brings the actual checks.

Related to MID-6913.
  • Loading branch information
mederly committed Aug 11, 2023
1 parent f43994a commit 4bb54d9
Show file tree
Hide file tree
Showing 12 changed files with 266 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,12 @@ public static FunctionLibrariesProfile of(@NotNull FunctionLibrariesProfileType
bean.getDecision(), "No decision in libraries profile %s", bean.getIdentifier())),
Collections.unmodifiableMap(libraryProfileMap));
}

public @NotNull AccessDecision decideFunctionAccess(@NotNull String libraryOid, @NotNull String functionName) {
var libraryProfile = libraryProfileMap.get(libraryOid);
if (libraryProfile == null) {
return getDefaultDecision();
}
return libraryProfile.decideFunctionAccess(functionName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,15 @@
import com.evolveum.midpoint.util.exception.ConfigurationException;
import com.evolveum.midpoint.xml.ns._public.common.common_3.FunctionLibraryProfileType;

import com.evolveum.midpoint.xml.ns._public.common.common_3.LibraryFunctionProfileType;

import org.jetbrains.annotations.NotNull;

import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import static com.evolveum.midpoint.prism.Referencable.getOid;
import static com.evolveum.midpoint.util.MiscUtil.configNonNull;
Expand All @@ -22,20 +28,35 @@
*/
public record FunctionLibraryProfile(
@NotNull String libraryOid,
@NotNull AccessDecision decision
)
@NotNull AccessDecision defaultDecision,
@NotNull Map<String, AccessDecision> functionProfileMap)
implements Serializable {

public static FunctionLibraryProfile of(@NotNull FunctionLibraryProfileType bean) throws ConfigurationException {
// TODO error locations

Map<String, AccessDecision> functionProfileMap = new HashMap<>();
for (LibraryFunctionProfileType functionProfileBean : bean.getFunction()) {
var name = configNonNull(functionProfileBean.getName(), "Unnamed function profile in %s", bean);
var decision = configNonNull(
functionProfileBean.getDecision(), "No decision in function profile '%s' in %s", name, bean);
functionProfileMap.put(name, AccessDecision.translate(decision));
}

return new FunctionLibraryProfile(
configNonNull(
getOid(bean.getRef()),
() -> "No library OID in function library profile at " + bean.asPrismContainerValue().getPath()),
AccessDecision.translate(
configNonNull(
bean.getDecision(),
() -> "No action decision in scripting profile at " + bean.asPrismContainerValue().getPath())));
() -> "No action decision in scripting profile at " + bean.asPrismContainerValue().getPath())),
Collections.unmodifiableMap(functionProfileMap));
}

@NotNull AccessDecision decideFunctionAccess(@NotNull String functionName) {
return Objects.requireNonNullElse(
functionProfileMap.get(functionName),
defaultDecision);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ public PrismValueDeltaSetTriple<V> evaluate(ExpressionEvaluationContext context,
OperationResult result = parentResult.createMinorSubresult(OP_EVALUATE);
try {
FunctionInLibrary function =
functionLibraryManager.getFunction(functionCallCI, context.getContextDescription(), result);
functionLibraryManager.findFunction(functionCallCI, context.getContextDescription(), result);

functionLibraryManager.checkCallAllowed(function, context.getExpressionProfile());

ExpressionProfile functionExpressionProfile =
functionLibraryManager.determineFunctionExpressionProfile(function.library(), result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import com.evolveum.midpoint.xml.ns._public.common.common_3.FunctionLibraryType;
import com.evolveum.prism.xml.ns._public.types_3.PolyStringType;

import static com.evolveum.midpoint.util.MiscUtil.argNonNull;

/**
* A "parsed form" of a {@link FunctionLibraryType}.
*
Expand All @@ -37,13 +39,18 @@ public class FunctionLibrary {

private static final Trace LOGGER = TraceManager.getTrace(FunctionLibrary.class);

@NotNull private final String oid;

/** The "raw" definition of the library. */
@NotNull private final FunctionLibraryType libraryBean;

/** An optimization: functions indexed by name. */
@NotNull private final Multimap<String, FunctionConfigItem> functionsByName = ArrayListMultimap.create();

private FunctionLibrary(@NotNull FunctionLibraryType libraryBean) throws ConfigurationException {
private FunctionLibrary(
@NotNull String oid,
@NotNull FunctionLibraryType libraryBean) throws ConfigurationException {
this.oid = oid;
this.libraryBean = libraryBean;
for (ExpressionType functionBean : libraryBean.getFunction()) {
// We cannot use "embedded" factory method here, because ExpressionType is not a Containerable - only a property.
Expand All @@ -58,9 +65,13 @@ private FunctionLibrary(@NotNull FunctionLibraryType libraryBean) throws Configu
/**
* Creates the library object from bean. Throws {@link ConfigurationException} for the most obvious problems.
* (No detailed checks, though.)
*
* Library must have an OID.
*/
public static FunctionLibrary of(@NotNull FunctionLibraryType bean) throws ConfigurationException {
return new FunctionLibrary(bean);
return new FunctionLibrary(
argNonNull(bean.getOid(), "Function Library has no OID %s", bean),
bean);
}

public @NotNull String getName() {
Expand Down Expand Up @@ -119,4 +130,8 @@ FunctionLibraryBinding createBinding(ExpressionFactory expressionFactory) {
public @NotNull PrismObject<FunctionLibraryType> getLibraryObject() {
return libraryBean.asPrismObject();
}

public @NotNull String getOid() {
return oid;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.evolveum.midpoint.repo.common.expression.Expression;
import com.evolveum.midpoint.repo.common.expression.ExpressionFactory;
import com.evolveum.midpoint.repo.common.expression.ExpressionSyntaxException;
import com.evolveum.midpoint.schema.AccessDecision;
import com.evolveum.midpoint.schema.config.FunctionConfigItem;
import com.evolveum.midpoint.schema.config.FunctionExpressionEvaluatorConfigItem;
import com.evolveum.midpoint.schema.expression.ExpressionProfile;
Expand Down Expand Up @@ -133,7 +134,7 @@ public void unregister() {
/**
* Finds a function by name. See {@link FunctionLibrary#findFunction(String, Collection, String)} for details.
*/
public @NotNull FunctionInLibrary getFunction(
public @NotNull FunctionInLibrary findFunction(
@NotNull FunctionExpressionEvaluatorConfigItem functionCall,
@NotNull String contextDesc,
@NotNull OperationResult result)
Expand Down Expand Up @@ -220,8 +221,33 @@ public void dumpContent() {
library.getLibraryObject(), result);
}

public void checkCallAllowed(
@NotNull FunctionInLibrary function,
// TODO change to not-null when profiles are ubiquitous
@Nullable ExpressionProfile expressionProfile) throws ConfigurationException, SecurityViolationException {
if (expressionProfile != null) {
var decision = expressionProfile.getLibrariesProfile().decideFunctionAccess(
function.library.getOid(),
function.function.getName());
if (decision != AccessDecision.ALLOW) {
throw new SecurityViolationException(
"Access to function library method %s %s (applied expression profile '%s', libraries profile '%s')"
.formatted(
function,
decision == AccessDecision.DENY ? "denied" : "not allowed",
expressionProfile.getIdentifier(),
expressionProfile.getLibrariesProfile().getIdentifier()));
}
}
}

public record FunctionInLibrary(
@NotNull FunctionConfigItem function,
@NotNull FunctionLibrary library) {

@Override
public String toString() {
return function.value().getName() + " in " + library.getLibraryObject();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.util.Objects;
import java.util.Set;

import com.evolveum.midpoint.model.common.expression.functions.FunctionLibraryManager.FunctionInLibrary;
import com.evolveum.midpoint.schema.expression.ExpressionProfile;
import com.evolveum.midpoint.task.api.Task;

Expand Down Expand Up @@ -74,6 +75,11 @@ public <V extends PrismValue, D extends ItemDefinition<?>> Object execute(
FunctionConfigItem function =
library.findFunction(functionName, paramNames, "custom function evaluation");

var callerProfile = ScriptExpressionEvaluationContext.getThreadLocalRequired().getExpressionProfile();
functionLibraryManager.checkCallAllowed(
new FunctionInLibrary(function, library),
callerProfile);

LOGGER.trace("function to execute {}", function);

var task = ScriptExpressionEvaluationContext.getTaskRequired();
Expand Down

0 comments on commit 4bb54d9

Please sign in to comment.