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..6a92b26c 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 inputList; + Map 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 nothingEvent = new Events() { @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,8 +194,19 @@ 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(); + validate(new Events() { + @Override + public void onFinished(Boolean result) { + } + @Override + public void onError(PerunException error) { + } + @Override + public void onLoadingStart() { + } + }); } }); setupRemoveButton(removeButton); 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..72d7589a 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 @@ -7,7 +7,6 @@ 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; -import cz.metacentrum.perun.wui.registrar.widgets.items.validators.SshKeysTextAreaValidator; import cz.metacentrum.perun.wui.registrar.widgets.items.validators.TextAreaValidator; import cz.metacentrum.perun.wui.widgets.boxes.ExtendedTextArea; import org.gwtbootstrap3.client.ui.html.Paragraph; @@ -27,12 +26,7 @@ public class TextArea extends PerunFormItemEditable { public TextArea(PerunForm form, ApplicationFormItemData item, String lang) { super(form, item, lang); - if ("urn:perun:user:attribute-def:def:sshPublicKey".equals(item.getFormItem().getPerunDestinationAttribute())) { - this.validator = new SshKeysTextAreaValidator(); - } else { - this.validator = new TextAreaValidator(); - } - + this.validator = new TextAreaValidator(); } protected Widget initWidget() { 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..6cd3a09a 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 @@ -7,7 +7,6 @@ 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; -import cz.metacentrum.perun.wui.registrar.widgets.items.validators.SshKeysTextFieldValidator; import cz.metacentrum.perun.wui.registrar.widgets.items.validators.TextFieldValidator; import cz.metacentrum.perun.wui.widgets.boxes.ExtendedTextBox; import org.gwtbootstrap3.client.ui.constants.ColumnSize; @@ -29,11 +28,7 @@ public class TextField extends PerunFormItemEditable { public TextField(PerunForm form, ApplicationFormItemData item, String lang) { super(form, item, lang); - if ("urn:perun:user:attribute-def:def:sshPublicKey".equals(item.getFormItem().getPerunDestinationAttribute())) { - this.validator = new SshKeysTextFieldValidator(); - } else { - this.validator = new TextFieldValidator(); - } + this.validator = new TextFieldValidator(); } @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..a4f42f1e 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,19 @@ 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.HashMap; +import java.util.Map; +import java.util.TreeMap; +import java.util.stream.Collectors; + /** * Validator for ListBox * @@ -13,11 +21,9 @@ */ 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]+)?$"); + final Map checkVals = new TreeMap<>(); + final Map indexMap = new HashMap<>(); + int validatingCounter = 0; @Override public boolean validateLocal(ListBox listBox) { @@ -26,34 +32,95 @@ public boolean validateLocal(ListBox listBox) { listBox.setRawStatus(getTransl().cantBeEmpty(), ValidationState.ERROR); return false; } + // FIXME - but it is probably not necessary as API should check it + for (ExtendedTextBox extendedTextBox : listBox.getListValue()) { + String sshKey = extendedTextBox.getValue(); + if (sshKey.contains(",")) { + setResult(Result.INVALID_FORMAT); + listBox.setStatus(getTransl().sshKeySeparatorNotAllowed(), ValidationState.ERROR); + return false; + } + } + listBox.setStatus(ValidationState.SUCCESS); + return true; + } + + @Override + public void validate(ListBox listBox, Events events) { + + if (!validateLocal(listBox)) { + events.onFinished(false); + return; + } + + if (listBox.getValue() == null || listBox.getValue().isEmpty()) { + events.onFinished(true); + return; + } + + checkVals.clear(); + indexMap.clear(); - if (listBox.getValue() != null && !listBox.getValue().isEmpty()) { + int counter = 1; + for (ExtendedTextBox extendedTextBox : listBox.getListValue()) { + String sshKey = extendedTextBox.getValue(); + indexMap.put(sshKey, counter); + checkVals.put(sshKey, ValidationState.NONE); + counter++; + } - String wrongValues = ""; - int index = 1; - for (ExtendedTextBox extendedTextBox : listBox.getListValue()) { - String sshKey = extendedTextBox.getValue(); + for (String sshKey : checkVals.keySet()) { - if (sshKey.contains(",")) { + UsersManager.validateSSHKey(sshKey, new JsonEvents() { + @Override + public void onFinished(JavaScriptObject result) { + validatingCounter--; + checkVals.put(sshKey, ValidationState.SUCCESS); + if (validatingCounter == 0) { + // last check trigger events + for (ValidationState state : checkVals.values()) { + // at least one key is invalid -> switch to error + if (ValidationState.ERROR == state) { + setResult(Result.INVALID_FORMAT); + listBox.setRawStatus(getTransl().incorrectFormatItemList() + "
" + checkVals.entrySet().stream().filter( entry -> (entry.getValue() == ValidationState.ERROR)).map((entry) -> indexMap.get(entry.getKey()) + ". " + + (entry.getKey().length() > 25 ? entry.getKey().substring(0,23) + "..." : entry.getKey())).collect(Collectors.joining("
")) + "
", ValidationState.ERROR); + // pass to outer event as fail + events.onFinished(false); + return; + } + } + // all values were OK -> trigger success + listBox.setStatus(ValidationState.SUCCESS); + events.onFinished(true); + } + } + + @Override + public void onError(PerunException error) { + validatingCounter--; + checkVals.put(sshKey, ValidationState.ERROR); + // set error immediately setResult(Result.INVALID_FORMAT); - listBox.setStatus(getTransl().sshKeySeparatorNotAllowed(), ValidationState.ERROR); - return false; + listBox.setRawStatus(getTransl().incorrectFormatItemList() + "
" + checkVals.entrySet().stream().filter( entry -> (entry.getValue() == ValidationState.ERROR)).map((entry) -> indexMap.get(entry.getKey()) + ". " + + (entry.getKey().length() > 25 ? entry.getKey().substring(0,23) + "..." : entry.getKey())).collect(Collectors.joining("
")) + "
", ValidationState.ERROR); + if (validatingCounter == 0) { + // if last pass to outer event as fail + events.onFinished(false); + } } - MatchResult matcher = regExp.exec(sshKey); - if (matcher == null) { - wrongValues += "
" + index + ". " + (sshKey.length() > 25 ? sshKey.substring(0, 23) + "..." : sshKey); + @Override + public void onLoadingStart() { + if (validatingCounter == 0) { + listBox.unsetStatus(); + events.onLoadingStart(); + setResult(Result.CHECKING_SSH); + listBox.setStatus(ValidationState.WARNING); + } + validatingCounter++; } - index++; - } - if (!wrongValues.isEmpty()) { - setResult(Result.INVALID_FORMAT); - listBox.setRawStatus(getTransl().incorrectFormatItemList() + " " + wrongValues + "", ValidationState.ERROR); - return false; - } - } + }); - listBox.setStatus(ValidationState.SUCCESS); - return true; + } } } 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 deleted file mode 100644 index 5f16d7b1..00000000 --- a/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/validators/SshKeysTextAreaValidator.java +++ /dev/null @@ -1,99 +0,0 @@ -package cz.metacentrum.perun.wui.registrar.widgets.items.validators; - -import com.google.gwt.regexp.shared.MatchResult; -import com.google.gwt.regexp.shared.RegExp; -import cz.metacentrum.perun.wui.registrar.widgets.items.TextArea; -import org.gwtbootstrap3.client.ui.constants.ValidationState; - -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - -/** - * @author Pavel Zlámal - */ -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]+)?$"); - - @Override - public boolean validateLocal(TextArea textArea) { - - if (textArea.isRequired() && isNullOrEmpty(textArea.getValue())) { - setResult(Result.EMPTY); - textArea.setRawStatus(getTransl().cantBeEmpty(), ValidationState.ERROR); - return false; - } - - if (!textArea.getBox().isValid()) { - setResult(Result.INVALID_FORMAT); - textArea.setStatus(getErrorMsgOrDefault(textArea), ValidationState.ERROR); - return false; - } - - if (textArea.getValue() != null && textArea.getValue().length() > textArea.MAX_LENGTH) { - setResult(Result.TOO_LONG); - textArea.setStatus(getTransl().tooLong(), ValidationState.ERROR); - return false; - } - - if (textArea.getValue() != null && !textArea.getValue().isEmpty()) { - - String sshKeys = textArea.getValue(); - - if (sshKeys.contains("\n")) { - setResult(Result.INVALID_FORMAT); - textArea.setStatus(getTransl().newlinesNotAllowed(), ValidationState.ERROR); - return false; - } - - if (sshKeys.contains(",,")) { - setResult(Result.INVALID_FORMAT); - textArea.setStatus(getTransl().tooMuchCommas(), ValidationState.ERROR); - return false; - } - - if (sshKeys.contains(", ") || sshKeys.contains(" ,")) { - setResult(Result.INVALID_FORMAT); - textArea.setStatus(getTransl().sshKeyNoSpaceAroundKeySeparator(), ValidationState.ERROR); - return false; - } - - // FIXME - this doesn't make sense anymore, as we have multiple different SSH keys prefixes, which needs to be checked. - /* - if (sshKeys.indexOf("ssh-") != sshKeys.lastIndexOf("ssh-")) { - // there are at least two keys - if (!sshKeys.contains(",ssh-") && !sshKeys.contains("\nssh-")) { - setResult(Result.INVALID_FORMAT); - textArea.setStatus(getTransl().sshKeyMissingDelimiter(), ValidationState.ERROR); - return false; - } - } - */ - - // normalize value just in case - sshKeys = sshKeys.replaceAll("(,)+", ","); - List keys = Arrays.stream(sshKeys.split(",")).collect(Collectors.toList()); - - for (String key : keys) { - MatchResult matcher = regExp.exec(key); - if (matcher == null) { - 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; - } - } - - } - - 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 deleted file mode 100644 index fd42724f..00000000 --- a/perun-wui-registrar/src/main/java/cz/metacentrum/perun/wui/registrar/widgets/items/validators/SshKeysTextFieldValidator.java +++ /dev/null @@ -1,93 +0,0 @@ -package cz.metacentrum.perun.wui.registrar.widgets.items.validators; - -import com.google.gwt.regexp.shared.MatchResult; -import com.google.gwt.regexp.shared.RegExp; -import cz.metacentrum.perun.wui.registrar.widgets.items.TextField; -import org.gwtbootstrap3.client.ui.constants.ValidationState; - -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - -/** - * @author Pavel Zlámal - */ -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]+)?$"); - - @Override - public boolean validateLocal(TextField textField) { - - if (textField.isRequired() && isNullOrEmpty(textField.getValue())) { - setResult(Result.EMPTY); - textField.setRawStatus(getTransl().cantBeEmpty(), ValidationState.ERROR); - return false; - } - - if (!textField.getBox().isValid()) { - setResult(Result.INVALID_FORMAT); - textField.setStatus(getErrorMsgOrDefault(textField), ValidationState.ERROR); - return false; - } - - if (textField.getValue() != null && textField.getValue().length() > textField.MAX_LENGTH) { - setResult(Result.TOO_LONG); - textField.setStatus(getTransl().tooLong(), ValidationState.ERROR); - return false; - } - - if (textField.getValue() != null && !textField.getValue().isEmpty()) { - - String sshKeys = textField.getValue(); - - if (sshKeys.contains(",,")) { - setResult(Result.INVALID_FORMAT); - textField.setStatus(getTransl().tooMuchCommas(), ValidationState.ERROR); - return false; - } - - if (sshKeys.contains(", ") || sshKeys.contains(" ,")) { - setResult(Result.INVALID_FORMAT); - textField.setStatus(getTransl().sshKeyNoSpaceAroundKeySeparator(), ValidationState.ERROR); - return false; - } - - // FIXME - this doesn't make sense anymore, as we have multiple different SSH keys prefixes, which needs to be checked. - /* - if (sshKeys.indexOf("ssh-") != sshKeys.lastIndexOf("ssh-")) { - // there are at least two keys - if (!sshKeys.contains(",ssh-")) { - setResult(Result.INVALID_FORMAT); - textField.setStatus(getTransl().sshKeyMissingCommaDelimiterTextField(), ValidationState.ERROR); - return false; - } - } - */ - - // normalize value just in case - sshKeys = sshKeys.replaceAll("(,)+", ","); - List keys = Arrays.stream(sshKeys.split(",")).collect(Collectors.toList()); - - for (String key : keys) { - MatchResult matcher = regExp.exec(key); - if (matcher == null) { - 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; - } - } - - } - - textField.setStatus(ValidationState.SUCCESS); - return true; - - } - -}