Skip to content

Commit

Permalink
Fix intra-cluster authentication (MID-5162)
Browse files Browse the repository at this point in the history
Also fixed the fix for TaskManagerQuartzImpl autowiring when profiling
is enabled.
  • Loading branch information
mederly committed Mar 1, 2019
1 parent 48c515c commit eb6903a
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 58 deletions.
Expand Up @@ -23,6 +23,7 @@
import com.evolveum.midpoint.prism.crypto.Protector;
import com.evolveum.midpoint.task.api.TaskManager;
import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
Expand Down Expand Up @@ -55,53 +56,60 @@ public class NodeAuthenticationEvaluatorImpl implements NodeAuthenticationEvalua

private static final String OPERATION_SEARCH_NODE = NodeAuthenticationEvaluatorImpl.class.getName() + ".searchNode";

public boolean authenticate(@Nullable String remoteName, String remoteAddress, @Nullable String credentials, String operation) {
public boolean authenticate(@Nullable String remoteName, String remoteAddress, @NotNull String credentials, String operation) {
LOGGER.debug("Checking if {} ({}) is a known node", remoteName, remoteAddress);
OperationResult result = new OperationResult(OPERATION_SEARCH_NODE);

ConnectionEnvironment connEnv = ConnectionEnvironment.create(SchemaConstants.CHANNEL_REST_URI);

try {
List<PrismObject<NodeType>> allNodes = repositoryService.searchObjects(NodeType.class, null, null, result);
List<PrismObject<NodeType>> matchingNodes = getMatchingNodes(allNodes, remoteName, remoteAddress, operation);

if (matchingNodes.size() == 1 || matchingNodes.size() >= 1 && taskManager.isLocalNodeClusteringEnabled()) {
List<PrismObject<NodeType>> matchingNodes = getMatchingNodes(allNodes, remoteName, remoteAddress);

if (matchingNodes.isEmpty()) {
LOGGER.debug("Authenticity cannot be established: No matching nodes for remote name '{}' and remote address '{}'",
remoteName, remoteAddress);
} else if (matchingNodes.size() > 1 && !taskManager.isLocalNodeClusteringEnabled()) {
LOGGER.debug("Authenticity cannot be established: More than one matching node for remote name '{}' and "
+ "remote address '{}' with local-node clustering disabled: {}", remoteName, remoteAddress, matchingNodes);
} else {
assert matchingNodes.size() == 1 || matchingNodes.size() > 1 && taskManager.isLocalNodeClusteringEnabled();
LOGGER.trace(
"Matching result: Node(s) {} recognized as known (remote host name {} or IP address {} matched). Attempting to execute the requested operation: {}",
allNodes, remoteName, remoteAddress, operation);
"Matching result: Node(s) {} recognized as known (remote host name {} or IP address {} matched).",
matchingNodes, remoteName, remoteAddress);
PrismObject<NodeType> actualNode = null;
if (credentials != null) {
for (PrismObject<NodeType> matchingNode : matchingNodes) {
ProtectedStringType encryptedSecret = matchingNode.asObjectable().getSecret();
if (encryptedSecret != null) {
String plainSecret;
try {
plainSecret = protector.decryptString(encryptedSecret);
} catch (EncryptionException e) {
LoggingUtils.logUnexpectedException(LOGGER, "Couldn't decrypt node secret for {}", e, matchingNode);
continue;
}
if (credentials.equals(plainSecret)) {
LOGGER.debug("Node secret matches for {}", matchingNode);
actualNode = matchingNode;
break;
} else {
LOGGER.debug("Node secret does not match for {}", matchingNode);
}
for (PrismObject<NodeType> matchingNode : matchingNodes) {
ProtectedStringType encryptedSecret = matchingNode.asObjectable().getSecret();
if (encryptedSecret != null) {
String plainSecret;
try {
plainSecret = protector.decryptString(encryptedSecret);
} catch (EncryptionException e) {
LoggingUtils.logUnexpectedException(LOGGER, "Couldn't decrypt node secret for {}", e, matchingNode);
continue;
}
if (credentials.equals(plainSecret)) {
LOGGER.debug("Node secret matches for {}", matchingNode);
actualNode = matchingNode;
break;
} else {
LOGGER.debug("Node secret does not match for {}", matchingNode);
}
} else {
LOGGER.debug("No secret known for node {}", matchingNode);
}
} else {
actualNode = matchingNodes.get(0);
}
if (actualNode != null) {
LOGGER.trace("Established authenticity for remote {}", actualNode);
NodeAuthenticationToken authNtoken = new NodeAuthenticationToken(actualNode, remoteAddress,
Collections.emptyList());
SecurityContextHolder.getContext().setAuthentication(authNtoken);
securityHelper.auditLoginSuccess(actualNode.asObjectable(), connEnv);
return true;
} else {
LOGGER.debug("Authenticity for {} couldn't be established: none of the secrets match", matchingNodes);
}
}

} catch (RuntimeException | SchemaException e) {
LOGGER.error("Unhandled exception when listing nodes");
LoggingUtils.logUnexpectedException(LOGGER, "Unhandled exception when listing nodes", e);
Expand All @@ -110,27 +118,24 @@ public boolean authenticate(@Nullable String remoteName, String remoteAddress, @
return false;
}

private List<PrismObject<NodeType>> getMatchingNodes(List<PrismObject<NodeType>> knownNodes, String remoteName, String remoteAddress, String operation) {
List<PrismObject<NodeType>> matchingNodes = new ArrayList<>();
for (PrismObject<NodeType> node : knownNodes) {
NodeType actualNode = node.asObjectable();
if (remoteName != null && remoteName.equalsIgnoreCase(actualNode.getHostname())) {
LOGGER.trace("The node {} was recognized as a known node (remote host name {} matched). Attempting to execute the requested operation: {} ",
actualNode.getName(), actualNode.getHostname(), operation);
matchingNodes.add(node);
continue;
}
if (actualNode.getIpAddress().contains(remoteAddress)) {
LOGGER.trace("The node {} was recognized as a known node (remote host address {} matched). Attempting to execute the requested operation: {} ",
actualNode.getName(), remoteAddress, operation);
matchingNodes.add(node);
continue;
}
}

return matchingNodes;
}



private List<PrismObject<NodeType>> getMatchingNodes(List<PrismObject<NodeType>> knownNodes, String remoteName,
String remoteAddress) {
List<PrismObject<NodeType>> matchingNodes = new ArrayList<>();
for (PrismObject<NodeType> node : knownNodes) {
NodeType actualNode = node.asObjectable();
if (remoteName != null && remoteName.equalsIgnoreCase(actualNode.getHostname())) {
LOGGER.trace("The node {} was recognized as a known node (remote host name {} matched).",
actualNode.getName(), actualNode.getHostname());
matchingNodes.add(node);
continue;
}
if (actualNode.getIpAddress().contains(remoteAddress)) {
LOGGER.trace("The node {} was recognized as a known node (remote host address {} matched).",
actualNode.getName(), remoteAddress);
matchingNodes.add(node);
continue;
}
}
return matchingNodes;
}
}
Expand Up @@ -740,4 +740,11 @@ String recordTaskThreadsDump(String taskOid, String cause, OperationResult paren
* Use only for tests. Otherwise considered to be an ugly hack.
*/
RunningTask createFakeRunningTask(Task task);

TaskHandler getHandler(String handlerUri);

NodeType getLocalNode();

// TEMPORARY HACK -- DO NOT USE OUTSIDE task-quartz-impl module
Object getWorkStateManager();
}
Expand Up @@ -42,6 +42,7 @@
import javax.xml.datatype.Duration;
import javax.xml.datatype.XMLGregorianCalendar;

import com.evolveum.midpoint.schema.util.ObjectTypeUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -91,7 +92,6 @@
import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator;
import com.evolveum.midpoint.task.api.LightweightTaskHandler;
import com.evolveum.midpoint.task.api.RunningTask;
import com.evolveum.midpoint.task.api.RunningTask;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.task.api.TaskCategory;
import com.evolveum.midpoint.task.api.TaskDeletionListener;
Expand Down Expand Up @@ -2548,5 +2548,8 @@ public RunningTaskQuartzImpl createFakeRunningTask(Task task) {
return createRunningTask(task);
}


@Override
public NodeType getLocalNode() {
return ObjectTypeUtil.asObjectable(clusterManager.getLocalNodeObject());
}
}
Expand Up @@ -34,6 +34,7 @@
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.NodeType;
import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType;
import org.apache.cxf.common.util.Base64Utility;
import org.apache.cxf.jaxrs.client.WebClient;
import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -110,14 +111,16 @@ public void execute(@NotNull NodeType node, @NotNull BiConsumer<WebClient, Opera
String url = baseUrl + "/ws/cluster";
LOGGER.debug("Going to execute '{}' on '{}'", context, url);
WebClient client = WebClient.create(url);
if (node.getSecret() == null) {
throw new SchemaException("No secret known for target node " + node.getNodeIdentifier());
NodeType localNode = taskManager.getLocalNode();
ProtectedStringType protectedSecret = localNode != null ? localNode.getSecret() : null;
if (protectedSecret == null) {
throw new SchemaException("No secret is set for local node " + localNode);
}
String secret;
try {
secret = protector.decryptString(node.getSecret());
} catch (EncryptionException e) {
throw new SystemException("Couldn't decrypt node secret: " + e.getMessage(), e);
throw new SystemException("Couldn't decrypt local node secret: " + e.getMessage(), e);
}
client.header("Authorization", RestAuthenticationMethod.CLUSTER.getMethod() + " " + Base64Utility.encode(secret.getBytes()));
code.accept(client, result);
Expand Down
Expand Up @@ -83,7 +83,7 @@ private TaskRunResult executePlainTaskHandler(RunningTask task, TaskPartitionDef

@NotNull
private TaskRunResult executeWorkBucketAwareTaskHandler(RunningTaskQuartzImpl task, TaskPartitionDefinitionType taskPartition, WorkBucketAwareTaskHandler handler, OperationResult executionResult) {
WorkStateManager workStateManager = ((TaskManagerQuartzImpl) taskManager).getWorkStateManager();
WorkStateManager workStateManager = (WorkStateManager) taskManager.getWorkStateManager();

if (task.getWorkState() != null && Boolean.TRUE.equals(task.getWorkState().isAllWorkComplete())) {
LOGGER.debug("Work is marked as complete; restarting it in task {}", task);
Expand Down Expand Up @@ -140,7 +140,7 @@ private TaskRunResult executeWorkBucketAwareTaskHandler(RunningTaskQuartzImpl ta
return runResult;
}
try {
((TaskManagerQuartzImpl) taskManager).getWorkStateManager().completeWorkBucket(task.getOid(), bucket.getSequentialNumber(), executionResult);
((WorkStateManager) taskManager.getWorkStateManager()).completeWorkBucket(task.getOid(), bucket.getSequentialNumber(), executionResult);
} catch (ObjectAlreadyExistsException | ObjectNotFoundException | SchemaException | RuntimeException e) {
LoggingUtils.logUnexpectedException(LOGGER, "Couldn't complete work bucket for task {}", e, task);
return createFailureTaskRunResult(task, "Couldn't complete work bucket: " + e.getMessage(), e);
Expand Down
Expand Up @@ -94,7 +94,7 @@ public TaskRunResult run(RunningTask task, TaskPartitionDefinitionType taskParti

partitions.sort(comparator);
for (TaskPartitionDefinitionType partition : partitions) {
TaskHandler handler = ((TaskManagerQuartzImpl) taskManager).getHandler(partition.getHandlerUri());
TaskHandler handler = taskManager.getHandler(partition.getHandlerUri());
TaskRunResult subHandlerResult = handlerExecutor.executeHandler((RunningTaskQuartzImpl) task, partition, handler, opResult);
// TaskRunResult subHandlerResult = handler.run(task, partition);
OperationResult subHandlerOpResult = subHandlerResult.getOperationResult();
Expand Down

0 comments on commit eb6903a

Please sign in to comment.