From cd39df0b71be31360ed132b28e2a7c696887ccc7 Mon Sep 17 00:00:00 2001
From: xflord <493294@mail.muni.cz>
Date: Wed, 25 Oct 2023 13:09:27 +0200
Subject: [PATCH] feat: backend ssh public key validation

* replaced regexp in SSH key validators with call to backend validation method
* this unifies the process and offers a more strict validation
---
 .../perun/wui/json/managers/UsersManager.java | 13 +++
 .../wui/registrar/widgets/items/ListBox.java  | 51 +++++++++--
 .../wui/registrar/widgets/items/TextArea.java | 39 ++++++--
 .../registrar/widgets/items/TextField.java    | 39 ++++++--
 .../validators/PerunFormItemValidator.java    |  1 +
 .../validators/SshKeysListBoxValidator.java   | 88 +++++++++++++------
 .../validators/SshKeysTextAreaValidator.java  | 71 ++++++++++-----
 .../validators/SshKeysTextFieldValidator.java | 72 ++++++++++-----
 8 files changed, 286 insertions(+), 88 deletions(-)

diff --git a/perun-wui-core/src/main/java/cz/metacentrum/perun/wui/json/managers/UsersManager.java b/perun-wui-core/src/main/java/cz/metacentrum/perun/wui/json/managers/UsersManager.java
index 3f91c4e7..9982caf3 100644
--- a/perun-wui-core/src/main/java/cz/metacentrum/perun/wui/json/managers/UsersManager.java
+++ b/perun-wui-core/src/main/java/cz/metacentrum/perun/wui/json/managers/UsersManager.java
@@ -252,6 +252,19 @@ public static Request validatePreferredEmailChange(int userId, String token, Jso
 
 	}
 
+	/**
+	 * Validate ssh public key, throws exception if validation fails
+	 *
+	 * @param sshKey ssh public key to verify
+	 * @return Request unique request
+	 */
+	public static Request validateSSHKey(String sshKey, JsonEvents events) {
+		JsonClient client = new JsonClient(events);
+		client.put("sshKey", sshKey);
+
+		return client.call(USERS_MANAGER + "validateSSHKey");
+	}
+
 	/**
 	 * Change password in selected namespace
 	 *
diff --git a/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/ListBox.java b/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/ListBox.java
index 81470486..4db932bd 100644
--- a/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/ListBox.java
+++ b/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/ListBox.java
@@ -9,6 +9,7 @@
 import com.google.gwt.user.client.ui.Widget;
 import com.google.gwt.user.client.ui.VerticalPanel;
 import cz.metacentrum.perun.wui.json.Events;
+import cz.metacentrum.perun.wui.model.PerunException;
 import cz.metacentrum.perun.wui.registrar.client.resources.PerunRegistrarResources;
 import cz.metacentrum.perun.wui.registrar.widgets.items.validators.ListBoxValidator;
 import cz.metacentrum.perun.wui.registrar.widgets.items.validators.SshKeysListBoxValidator;
@@ -19,7 +20,9 @@
 import org.gwtbootstrap3.client.ui.html.Paragraph;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Represents ListBox form item.
@@ -30,6 +33,7 @@ public class ListBox extends WidgetBox {
 
 	private final ListBoxValidator validator;
 	List<ExtendedTextBox> inputList;
+	Map<ExtendedTextBox, BlurHandler> handlers;
 
 	public ListBox(PerunForm form, ApplicationFormItemData item, String lang) {
 		super(form, item, lang);
@@ -78,6 +82,7 @@ public boolean validateLocal() {
 	@Override
 	protected Widget initWidget() {
 		inputList = new ArrayList<>();
+		handlers = new HashMap<>();
 		return super.initWidget();
 	}
 
@@ -95,13 +100,48 @@ protected void setValidationTriggers() {
 		if (isOnlyPreview()) {
 			return;
 		}
-		for (ExtendedTextBox input : inputList) {
-			input.addBlurHandler(new BlurHandler() {
+		if ("urn:perun:user:attribute-def:def:sshPublicKey".equals(this.getItemData().getFormItem().getPerunDestinationAttribute())) {
+			final Events<Boolean> nothingEvent = new Events<Boolean>() {
 				@Override
-				public void onBlur(BlurEvent event) {
-					validateLocal();
+				public void onFinished(Boolean result) {
+
+				}
+
+				@Override
+				public void onError(PerunException error) {
+
 				}
-			});
+
+				@Override
+				public void onLoadingStart() {
+
+				}
+			};
+
+			for (ExtendedTextBox input : inputList) {
+
+				if (!handlers.containsKey(input)) {
+					BlurHandler handler = new BlurHandler() {
+						@Override
+						public void onBlur(BlurEvent event) {
+							validate(nothingEvent);
+						}
+					};
+					input.addBlurHandler(handler);
+					handlers.put(input, handler);
+				}
+
+			}
+
+		} else {
+			for (ExtendedTextBox input : inputList) {
+				input.addBlurHandler(new BlurHandler() {
+					@Override
+					public void onBlur(BlurEvent event) {
+						validateLocal();
+					}
+				});
+			}
 		}
 	}
 
@@ -154,6 +194,7 @@ protected void generateItemWithRemoveButton(VerticalPanel vp) {
 		PerunButton removeButton = new PerunButton("", new ClickHandler() {
 			public void onClick(ClickEvent event) {
 				inputList.remove(input);
+				handlers.remove(input);
 				vp.remove(hp);
 				validateLocal();
 			}
diff --git a/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/TextArea.java b/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/TextArea.java
index 8e7c7062..e18011ab 100644
--- a/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/TextArea.java
+++ b/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/TextArea.java
@@ -4,6 +4,7 @@
 import com.google.gwt.event.dom.client.BlurHandler;
 import com.google.gwt.user.client.ui.Widget;
 import cz.metacentrum.perun.wui.json.Events;
+import cz.metacentrum.perun.wui.model.PerunException;
 import cz.metacentrum.perun.wui.model.beans.ApplicationFormItemData;
 import cz.metacentrum.perun.wui.registrar.widgets.PerunForm;
 import cz.metacentrum.perun.wui.registrar.widgets.items.validators.PerunFormItemValidator;
@@ -87,12 +88,38 @@ public void setValidationTriggers() {
 		if (isOnlyPreview()) {
 			return;
 		}
-		getBox().addBlurHandler(new BlurHandler() {
-			@Override
-			public void onBlur(BlurEvent event) {
-				validateLocal();
-			}
-		});
+		if ("urn:perun:user:attribute-def:def:sshPublicKey".equals(this.getItemData().getFormItem().getPerunDestinationAttribute())) {
+			final Events<Boolean> nothingEvent = new Events<Boolean>() {
+				@Override
+				public void onFinished(Boolean result) {
+
+				}
+
+				@Override
+				public void onError(PerunException error) {
+
+				}
+
+				@Override
+				public void onLoadingStart() {
+
+				}
+			};
+
+			getBox().addBlurHandler(new BlurHandler() {
+				@Override
+				public void onBlur(BlurEvent event) {
+					validate(nothingEvent);
+				}
+			});
+		} else {
+			getBox().addBlurHandler(new BlurHandler() {
+				@Override
+				public void onBlur(BlurEvent event) {
+					validateLocal();
+				}
+			});
+		}
 	}
 
 	@Override
diff --git a/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/TextField.java b/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/TextField.java
index 0a244c8c..389e2598 100644
--- a/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/TextField.java
+++ b/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/TextField.java
@@ -4,6 +4,7 @@
 import com.google.gwt.event.dom.client.BlurHandler;
 import com.google.gwt.user.client.ui.Widget;
 import cz.metacentrum.perun.wui.json.Events;
+import cz.metacentrum.perun.wui.model.PerunException;
 import cz.metacentrum.perun.wui.model.beans.ApplicationFormItemData;
 import cz.metacentrum.perun.wui.registrar.widgets.PerunForm;
 import cz.metacentrum.perun.wui.registrar.widgets.items.validators.PerunFormItemValidator;
@@ -104,12 +105,38 @@ public void setValidationTriggers() {
 		if (isOnlyPreview()) {
 			return;
 		}
-		getBox().addBlurHandler(new BlurHandler() {
-			@Override
-			public void onBlur(BlurEvent event) {
-				validateLocal();
-			}
-		});
+		if ("urn:perun:user:attribute-def:def:sshPublicKey".equals(this.getItemData().getFormItem().getPerunDestinationAttribute())) {
+			final Events<Boolean> nothingEvent = new Events<Boolean>() {
+				@Override
+				public void onFinished(Boolean result) {
+
+				}
+
+				@Override
+				public void onError(PerunException error) {
+
+				}
+
+				@Override
+				public void onLoadingStart() {
+
+				}
+			};
+
+			getBox().addBlurHandler(new BlurHandler() {
+				@Override
+				public void onBlur(BlurEvent event) {
+					validate(nothingEvent);
+				}
+			});
+		} else {
+			getBox().addBlurHandler(new BlurHandler() {
+				@Override
+				public void onBlur(BlurEvent event) {
+					validateLocal();
+				}
+			});
+		}
 	}
 
 	@Override
diff --git a/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/validators/PerunFormItemValidator.java b/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/validators/PerunFormItemValidator.java
index d408b90a..ea1f492a 100644
--- a/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/validators/PerunFormItemValidator.java
+++ b/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/validators/PerunFormItemValidator.java
@@ -20,6 +20,7 @@ enum Result {
 		INVALID_FORMAT_EMAIL,
 		LOGIN_NOT_AVAILABLE,
 		CHECKING_LOGIN,
+		CHECKING_SSH,
 		CANT_CHECK_LOGIN,
 		EMPTY_PASSWORD,
 		PASSWORD_TOO_SHORT,
diff --git a/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/validators/SshKeysListBoxValidator.java b/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/validators/SshKeysListBoxValidator.java
index d4f4c096..b37e3dc1 100644
--- a/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/validators/SshKeysListBoxValidator.java
+++ b/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/validators/SshKeysListBoxValidator.java
@@ -1,11 +1,18 @@
 package cz.metacentrum.perun.wui.registrar.widgets.items.validators;
 
-import com.google.gwt.regexp.shared.MatchResult;
-import com.google.gwt.regexp.shared.RegExp;
+import com.google.gwt.core.client.JavaScriptObject;
+import cz.metacentrum.perun.wui.json.Events;
+import cz.metacentrum.perun.wui.json.JsonEvents;
+import cz.metacentrum.perun.wui.json.managers.UsersManager;
+import cz.metacentrum.perun.wui.model.PerunException;
 import cz.metacentrum.perun.wui.registrar.widgets.items.ListBox;
 import cz.metacentrum.perun.wui.widgets.boxes.ExtendedTextBox;
 import org.gwtbootstrap3.client.ui.constants.ValidationState;
 
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
+
 /**
  * Validator for ListBox
  *
@@ -13,11 +20,7 @@
  */
 public class SshKeysListBoxValidator extends ListBoxValidator {
 
-	RegExp regExp = RegExp.compile("^(" +
-		"(ssh-(rsa|dss|ed25519)(-cert-v01@openssh.com)?)|" +
-		"(sk-(ssh-ed25519|ecdsa-sha2-nistp256)(-cert-v01)?@openssh.com)|" +
-		"(ecdsa-sha2-nistp(256|384|521)(-cert-v01@openssh.com)?))" +
-		" (([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?)( [^,\n]+)?$");
+	Map<Integer, String> wrongVals = new TreeMap<>();
 
 	@Override
 	public boolean validateLocal(ListBox listBox) {
@@ -26,34 +29,61 @@ public boolean validateLocal(ListBox listBox) {
 			listBox.setRawStatus(getTransl().cantBeEmpty(), ValidationState.ERROR);
 			return false;
 		}
+		listBox.setStatus(ValidationState.SUCCESS);
+		return true;
+	}
 
-		if (listBox.getValue() != null && !listBox.getValue().isEmpty()) {
+	@Override
+	public void validate(ListBox listBox, Events<Boolean> events) {
+		events.onLoadingStart();
 
-			String wrongValues = "";
-			int index = 1;
-			for (ExtendedTextBox extendedTextBox : listBox.getListValue()) {
-				String sshKey = extendedTextBox.getValue();
+		if (!validateLocal(listBox)) {
+			events.onFinished(false);
+			return;
+		}
 
-				if (sshKey.contains(",")) {
-					setResult(Result.INVALID_FORMAT);
-					listBox.setStatus(getTransl().sshKeySeparatorNotAllowed(), ValidationState.ERROR);
-					return false;
-				}
+		if (listBox.getValue() == null || listBox.getValue().isEmpty()) {
+			events.onFinished(true);
+			return;
+		}
 
-				MatchResult matcher = regExp.exec(sshKey);
-				if (matcher == null) {
-					wrongValues += "<br>" + index + ". " + (sshKey.length() > 25 ? sshKey.substring(0, 23) + "..." : sshKey);
-				}
-				index++;
-			}
-			if (!wrongValues.isEmpty()) {
+		wrongVals.clear();
+		int index = 1;
+		for (ExtendedTextBox extendedTextBox : listBox.getListValue()) {
+			String sshKey = extendedTextBox.getValue();
+
+			if (sshKey.contains(",")) {
 				setResult(Result.INVALID_FORMAT);
-				listBox.setRawStatus(getTransl().incorrectFormatItemList() + " <b>" + wrongValues + "</b>", ValidationState.ERROR);
-				return false;
+				listBox.setStatus(getTransl().sshKeySeparatorNotAllowed(), ValidationState.ERROR);
+				events.onFinished(false);
 			}
-		}
 
-		listBox.setStatus(ValidationState.SUCCESS);
-		return true;
+			int currIndex = index;
+			UsersManager.validateSSHKey(sshKey, new JsonEvents() {
+				@Override
+				public void onFinished(JavaScriptObject result) {
+					if (wrongVals.isEmpty()) {
+						events.onFinished(true);
+						listBox.setStatus(ValidationState.SUCCESS);
+					}
+				}
+
+				@Override
+				public void onError(PerunException error) {
+					wrongVals.put(currIndex, sshKey);
+                    setResult(Result.INVALID_FORMAT);
+                    listBox.setRawStatus(getTransl().incorrectFormatItemList() + " <b> <br>" + wrongVals.entrySet().stream().map((entry) -> entry.getKey() + ". " +
+						(entry.getValue().length() > 25 ? entry.getValue().substring(0,23) + "..." : entry.getValue())).collect(Collectors.joining("<br>")) + "</b>", ValidationState.ERROR);
+                    events.onFinished(false);
+                }
+
+				@Override
+				public void onLoadingStart() {
+					setResult(Result.CHECKING_SSH);
+					listBox.unsetStatus();
+				}
+			});
+			index++;
+		}
 	}
 }
diff --git a/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/validators/SshKeysTextAreaValidator.java b/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/validators/SshKeysTextAreaValidator.java
index 5f16d7b1..cee1c09c 100644
--- a/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/validators/SshKeysTextAreaValidator.java
+++ b/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/validators/SshKeysTextAreaValidator.java
@@ -1,7 +1,10 @@
 package cz.metacentrum.perun.wui.registrar.widgets.items.validators;
 
-import com.google.gwt.regexp.shared.MatchResult;
-import com.google.gwt.regexp.shared.RegExp;
+import com.google.gwt.core.client.JavaScriptObject;
+import cz.metacentrum.perun.wui.json.Events;
+import cz.metacentrum.perun.wui.json.JsonEvents;
+import cz.metacentrum.perun.wui.json.managers.UsersManager;
+import cz.metacentrum.perun.wui.model.PerunException;
 import cz.metacentrum.perun.wui.registrar.widgets.items.TextArea;
 import org.gwtbootstrap3.client.ui.constants.ValidationState;
 
@@ -13,12 +16,7 @@
  * @author Pavel Zlámal <zlamal@cesnet.cz>
  */
 public class SshKeysTextAreaValidator extends TextAreaValidator {
-
-	RegExp regExp = RegExp.compile("^(" +
-			"(ssh-(rsa|dss|ed25519)(-cert-v01@openssh.com)?)|" +
-			"(sk-(ssh-ed25519|ecdsa-sha2-nistp256)(-cert-v01)?@openssh.com)|" +
-			"(ecdsa-sha2-nistp(256|384|521)(-cert-v01@openssh.com)?))" +
-			" (([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?)( [^,\n]+)?$");
+	String wrongValues = "";
 
 	@Override
 	public boolean validateLocal(TextArea textArea) {
@@ -74,26 +72,57 @@ public boolean validateLocal(TextArea textArea) {
 				}
 			}
 			*/
+		}
+
+		textArea.setStatus(ValidationState.SUCCESS);
+		return true;
 
-			// normalize value just in case
-			sshKeys = sshKeys.replaceAll("(,)+", ",");
-			List<String> keys = Arrays.stream(sshKeys.split(",")).collect(Collectors.toList());
+	}
+	@Override
+	public void validate(TextArea textArea, Events<Boolean> events) {
+		events.onLoadingStart();
+
+		if (!validateLocal(textArea)) {
+			events.onFinished(false);
+			return;
+		}
+
+		if (textArea.getValue() == null || textArea.getValue().isEmpty()) {
+			events.onFinished(true);
+			return;
+		}
 
-			for (String key : keys) {
-				MatchResult matcher = regExp.exec(key);
-				if (matcher == null) {
+		String sshKeys = textArea.getValue();
+		// normalize value just in case
+		sshKeys = sshKeys.replaceAll("(,)+", ",");
+		List<String> keys = Arrays.stream(sshKeys.split(",")).collect(Collectors.toList());
+		wrongValues = "";
+
+		for (String key : keys) {
+			UsersManager.validateSSHKey(key, new JsonEvents() {
+				@Override
+				public void onFinished(JavaScriptObject result) {
+					if (wrongValues.isEmpty()) {
+						textArea.setStatus(ValidationState.SUCCESS);
+						events.onFinished(true);
+					}
+				}
+
+				@Override
+				public void onError(PerunException error) {
+					wrongValues += key;
 					int length = Math.min(key.length(), 30);
 					textArea.setRawStatus(getTransl().sshKeyFormat(key.substring(0, length)+((length == 30) ? "..." : "")), ValidationState.ERROR);
 					setResult(Result.INVALID_FORMAT);
-					return false;
+					events.onFinished(false);
 				}
-			}
 
+				@Override
+				public void onLoadingStart() {
+					setResult(Result.CHECKING_SSH);
+					textArea.unsetStatus();
+				}
+			});
 		}
-
-		textArea.setStatus(ValidationState.SUCCESS);
-		return true;
-
 	}
-
 }
diff --git a/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/validators/SshKeysTextFieldValidator.java b/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/validators/SshKeysTextFieldValidator.java
index fd42724f..9e2fc1fc 100644
--- a/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/validators/SshKeysTextFieldValidator.java
+++ b/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/validators/SshKeysTextFieldValidator.java
@@ -1,7 +1,10 @@
 package cz.metacentrum.perun.wui.registrar.widgets.items.validators;
 
-import com.google.gwt.regexp.shared.MatchResult;
-import com.google.gwt.regexp.shared.RegExp;
+import com.google.gwt.core.client.JavaScriptObject;
+import cz.metacentrum.perun.wui.json.Events;
+import cz.metacentrum.perun.wui.json.JsonEvents;
+import cz.metacentrum.perun.wui.json.managers.UsersManager;
+import cz.metacentrum.perun.wui.model.PerunException;
 import cz.metacentrum.perun.wui.registrar.widgets.items.TextField;
 import org.gwtbootstrap3.client.ui.constants.ValidationState;
 
@@ -13,12 +16,7 @@
  * @author Pavel Zlámal <zlamal@cesnet.cz>
  */
 public class SshKeysTextFieldValidator extends TextFieldValidator {
-
-	RegExp regExp = RegExp.compile("^(" +
-			"(ssh-(rsa|dss|ed25519)(-cert-v01@openssh.com)?)|" +
-			"(sk-(ssh-ed25519|ecdsa-sha2-nistp256)(-cert-v01)?@openssh.com)|" +
-			"(ecdsa-sha2-nistp(256|384|521)(-cert-v01@openssh.com)?))" +
-			" (([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?)( [^,\n]+)?$");
+	String wrongValues = "";
 
 	@Override
 	public boolean validateLocal(TextField textField) {
@@ -69,25 +67,57 @@ public boolean validateLocal(TextField textField) {
 			}
 			*/
 
-			// normalize value just in case
-			sshKeys = sshKeys.replaceAll("(,)+", ",");
-			List<String> keys = Arrays.stream(sshKeys.split(",")).collect(Collectors.toList());
+		}
+
+		textField.setStatus(ValidationState.SUCCESS);
+		return true;
+
+	}
+
+	@Override
+	public void validate(TextField textField, Events<Boolean> events) {
+		events.onLoadingStart();
 
-			for (String key : keys) {
-				MatchResult matcher = regExp.exec(key);
-				if (matcher == null) {
+		if (!validateLocal(textField)) {
+			events.onFinished(false);
+			return;
+		}
+
+		if (textField.getValue() == null || textField.getValue().isEmpty()) {
+			events.onFinished(true);
+			return;
+		}
+
+		String sshKeys = textField.getValue();
+		// normalize value just in case
+		sshKeys = sshKeys.replaceAll("(,)+", ",");
+		List<String> keys = Arrays.stream(sshKeys.split(",")).collect(Collectors.toList());
+		wrongValues = "";
+		for (String key : keys) {
+			UsersManager.validateSSHKey(key, new JsonEvents() {
+				@Override
+				public void onFinished(JavaScriptObject result) {
+					if (wrongValues.isEmpty()) {
+						textField.setStatus(ValidationState.SUCCESS);
+						events.onFinished(true);
+					}
+				}
+
+				@Override
+				public void onError(PerunException error) {
+					wrongValues += key;
 					int length = Math.min(key.length(), 30);
 					textField.setRawStatus(getTransl().sshKeyFormat(key.substring(0, length)+((length == 30) ? "..." : "")), ValidationState.ERROR);
 					setResult(Result.INVALID_FORMAT);
-					return false;
+					events.onFinished(false);
 				}
-			}
 
+				@Override
+				public void onLoadingStart() {
+					setResult(Result.CHECKING_SSH);
+					textField.unsetStatus();
+				}
+			});
 		}
-
-		textField.setStatus(ValidationState.SUCCESS);
-		return true;
-
 	}
-
 }