Skip to content

Commit

Permalink
feat: backend ssh public key validation
Browse files Browse the repository at this point in the history
* replaced regexp in SSH key validators with call to backend validation method
* this unifies the process and offers a more strict validation
  • Loading branch information
xflord committed Oct 30, 2023
1 parent 585f94f commit 7a7af21
Show file tree
Hide file tree
Showing 8 changed files with 265 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,13 @@ public static Request validatePreferredEmailChange(int userId, String token, Jso

}

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
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -95,13 +96,42 @@ protected void setValidationTriggers() {
if (isOnlyPreview()) {
return;
}
for (ExtendedTextBox input : inputList) {
input.addBlurHandler(new BlurHandler() {
if (this.validator instanceof SshKeysListBoxValidator) {
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) {
input.addBlurHandler(new BlurHandler() {
@Override
public void onBlur(BlurEvent event) {
validate(nothingEvent);
}
});
}

} else {
for (ExtendedTextBox input : inputList) {
input.addBlurHandler(new BlurHandler() {
@Override
public void onBlur(BlurEvent event) {
validateLocal();
}
});
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -87,12 +88,38 @@ public void setValidationTriggers() {
if (isOnlyPreview()) {
return;
}
getBox().addBlurHandler(new BlurHandler() {
@Override
public void onBlur(BlurEvent event) {
validateLocal();
}
});
if (this.validator instanceof SshKeysTextAreaValidator) {
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -104,12 +105,38 @@ public void setValidationTriggers() {
if (isOnlyPreview()) {
return;
}
getBox().addBlurHandler(new BlurHandler() {
@Override
public void onBlur(BlurEvent event) {
validateLocal();
}
});
if (this.validator instanceof SshKeysTextFieldValidator) {
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ enum Result {
INVALID_FORMAT_EMAIL,
LOGIN_NOT_AVAILABLE,
CHECKING_LOGIN,
CHECKING_SSH,
CANT_CHECK_LOGIN,
EMPTY_PASSWORD,
PASSWORD_TOO_SHORT,
Expand Down
Original file line number Diff line number Diff line change
@@ -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.ListBox;
import cz.metacentrum.perun.wui.widgets.boxes.ExtendedTextBox;
import org.gwtbootstrap3.client.ui.constants.ValidationState;
Expand All @@ -13,11 +16,7 @@
*/
public class SshKeysListBoxValidator extends ListBoxValidator {

RegExp regExp = RegExp.compile("^(" +
"(ssh-(rsa|dss|ed25519)([email protected])?)|" +
"(sk-(ssh-ed25519|ecdsa-sha2-nistp256)(-cert-v01)[email protected])|" +
"(ecdsa-sha2-nistp(256|384|521)([email protected])?))" +
" (([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?)( [^,\n]+)?$");
String wrongValues = "";

@Override
public boolean validateLocal(ListBox listBox) {
Expand All @@ -26,34 +25,61 @@ public boolean validateLocal(ListBox listBox) {
listBox.setRawStatus(getTransl().cantBeEmpty(), ValidationState.ERROR);
return false;
}
listBox.setStatus(ValidationState.SUCCESS);
return true;
}

@Override
public void validate(ListBox listBox, Events<Boolean> events) {
events.onLoadingStart();

if (listBox.getValue() != null && !listBox.getValue().isEmpty()) {
if (!validateLocal(listBox)) {
events.onFinished(false);
return;
}

String wrongValues = "";
int index = 1;
for (ExtendedTextBox extendedTextBox : listBox.getListValue()) {
String sshKey = extendedTextBox.getValue();
if (listBox.getValue() == null || listBox.getValue().isEmpty()) {
events.onFinished(true);
return;
}

if (sshKey.contains(",")) {
setResult(Result.INVALID_FORMAT);
listBox.setStatus(getTransl().sshKeySeparatorNotAllowed(), ValidationState.ERROR);
return false;
}

MatchResult matcher = regExp.exec(sshKey);
if (matcher == null) {
wrongValues += "<br>" + index + ". " + (sshKey.length() > 25 ? sshKey.substring(0, 23) + "..." : sshKey);
}
index++;
}
if (!wrongValues.isEmpty()) {
wrongValues = "";
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 (wrongValues.isEmpty()) {
events.onFinished(true);
listBox.setStatus(ValidationState.SUCCESS);
}
}

@Override
public void onError(PerunException error) {
wrongValues += "<br>" + currIndex + ". " + (sshKey.length() > 25 ? sshKey.substring(0, 23) + "..." : sshKey);
setResult(Result.INVALID_FORMAT);
listBox.setRawStatus(getTransl().incorrectFormatItemList() + " <b>" + wrongValues + "</b>", ValidationState.ERROR);
events.onFinished(false);
}

@Override
public void onLoadingStart() {
setResult(Result.CHECKING_SSH);
listBox.unsetStatus();
}
});
index++;
}
}
}
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -13,12 +16,7 @@
* @author Pavel Zlámal <[email protected]>
*/
public class SshKeysTextAreaValidator extends TextAreaValidator {

RegExp regExp = RegExp.compile("^(" +
"(ssh-(rsa|dss|ed25519)([email protected])?)|" +
"(sk-(ssh-ed25519|ecdsa-sha2-nistp256)(-cert-v01)[email protected])|" +
"(ecdsa-sha2-nistp(256|384|521)([email protected])?))" +
" (([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?)( [^,\n]+)?$");
String wrongValues = "";

@Override
public boolean validateLocal(TextArea textArea) {
Expand Down Expand Up @@ -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;

}

}
Loading

0 comments on commit 7a7af21

Please sign in to comment.