Restrict typeable characters in SSH username
Since the SSH username does not permit character such as space, do not let the user enter such characters when they are filling out the field. Instead eat the key event so they realize that space is not an acceptable character, and try something else. Because JavaScript reports the arrow keys as normal ASCII characters (e.g. left arrow appears the same to us as '%') we have to allow some invalid characters to enter the box, but catch them before we submit to the server. This way we can still allow use of useful features in the box (like inline editing) but quickly tell the user if we don't like their name without needing to wait for a server RPC. Of course we still perform final validation on the server, in case the client is out of date or was hacked to allow a wider range than we want to allow in the field. Change-Id: I8587ff08ae81e6ca2841b3a6162923cf502d5e12 Signed-off-by: Shawn O. Pearce <sop@google.com> Reviewed-by: Grzegorz Kossakowski <grek@google.com>
This commit is contained in:
@@ -47,6 +47,7 @@ public interface AccountConstants extends Constants {
|
||||
|
||||
String sshUserName();
|
||||
String buttonChangeSshUserName();
|
||||
String invalidSshUserName();
|
||||
|
||||
String sshKeyInvalid();
|
||||
String sshKeyAlgorithm();
|
||||
|
||||
@@ -27,6 +27,7 @@ buttonAddSshKey = Add
|
||||
|
||||
sshUserName = SSH Username
|
||||
buttonChangeSshUserName = Change Username
|
||||
invalidSshUserName = SSH username must contain only letters, numbers, _, - or .
|
||||
|
||||
sshKeyInvalid = Invalid Key
|
||||
sshKeyAlgorithm = Algorithm
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.google.gerrit.client.reviewdb.Account;
|
||||
import com.google.gerrit.client.reviewdb.AccountSshKey;
|
||||
import com.google.gerrit.client.rpc.GerritCallback;
|
||||
import com.google.gerrit.client.rpc.InvalidSshKeyException;
|
||||
import com.google.gerrit.client.rpc.InvalidSshUserNameException;
|
||||
import com.google.gerrit.client.ui.FancyFlexTable;
|
||||
import com.google.gerrit.client.ui.SmallHeading;
|
||||
import com.google.gerrit.client.ui.TextSaveButtonListener;
|
||||
@@ -29,6 +30,9 @@ import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.dom.client.Element;
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
import com.google.gwt.event.dom.client.ClickHandler;
|
||||
import com.google.gwt.event.dom.client.KeyCodes;
|
||||
import com.google.gwt.event.dom.client.KeyPressEvent;
|
||||
import com.google.gwt.event.dom.client.KeyPressHandler;
|
||||
import com.google.gwt.i18n.client.LocaleInfo;
|
||||
import com.google.gwt.user.client.Command;
|
||||
import com.google.gwt.user.client.DOM;
|
||||
@@ -43,6 +47,7 @@ import com.google.gwt.user.client.ui.HasHorizontalAlignment;
|
||||
import com.google.gwt.user.client.ui.HorizontalPanel;
|
||||
import com.google.gwt.user.client.ui.Panel;
|
||||
import com.google.gwt.user.client.ui.RootPanel;
|
||||
import com.google.gwt.user.client.ui.TextBox;
|
||||
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
|
||||
@@ -93,6 +98,7 @@ class SshPanel extends Composite {
|
||||
if (Gerrit.isSignedIn()) {
|
||||
userNameTxt.setText(Gerrit.getUserAccount().getSshUserName());
|
||||
}
|
||||
userNameTxt.addKeyPressHandler(new SshUserNameValidator());
|
||||
userNameTxt.addStyleName("gerrit-SshPanel-username");
|
||||
userNameTxt.setVisibleLength(16);
|
||||
userNameTxt.setReadOnly(!canEditSshUserName());
|
||||
@@ -229,7 +235,7 @@ class SshPanel extends Composite {
|
||||
}
|
||||
|
||||
void doChangeUserName() {
|
||||
if(!canEditSshUserName()){
|
||||
if (!canEditSshUserName()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -237,6 +243,10 @@ class SshPanel extends Composite {
|
||||
if ("".equals(newName)) {
|
||||
newName = null;
|
||||
}
|
||||
if (newName != null && !newName.matches(Account.SSH_USER_NAME_PATTERN)) {
|
||||
invalidUserName();
|
||||
return;
|
||||
}
|
||||
|
||||
userNameTxt.setEnabled(false);
|
||||
changeUserName.setEnabled(false);
|
||||
@@ -256,11 +266,20 @@ class SshPanel extends Composite {
|
||||
public void onFailure(final Throwable caught) {
|
||||
userNameTxt.setEnabled(true);
|
||||
changeUserName.setEnabled(true);
|
||||
super.onFailure(caught);
|
||||
if (InvalidSshUserNameException.MESSAGE.equals(caught.getMessage())) {
|
||||
invalidUserName();
|
||||
} else {
|
||||
super.onFailure(caught);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void invalidUserName() {
|
||||
userNameTxt.setFocus(true);
|
||||
new ErrorDialog(Util.C.invalidSshUserName()).center();
|
||||
}
|
||||
|
||||
void doBrowse() {
|
||||
browse.setEnabled(false);
|
||||
if (!loadedApplet) {
|
||||
@@ -423,6 +442,53 @@ class SshPanel extends Composite {
|
||||
addKeyBlock.setVisible(show);
|
||||
}
|
||||
|
||||
private final class SshUserNameValidator implements KeyPressHandler {
|
||||
@Override
|
||||
public void onKeyPress(final KeyPressEvent event) {
|
||||
final char code = event.getCharCode();
|
||||
switch (code) {
|
||||
case KeyCodes.KEY_ALT:
|
||||
case KeyCodes.KEY_BACKSPACE:
|
||||
case KeyCodes.KEY_CTRL:
|
||||
case KeyCodes.KEY_DELETE:
|
||||
case KeyCodes.KEY_DOWN:
|
||||
case KeyCodes.KEY_END:
|
||||
case KeyCodes.KEY_ENTER:
|
||||
case KeyCodes.KEY_ESCAPE:
|
||||
case KeyCodes.KEY_HOME:
|
||||
case KeyCodes.KEY_LEFT:
|
||||
case KeyCodes.KEY_PAGEDOWN:
|
||||
case KeyCodes.KEY_PAGEUP:
|
||||
case KeyCodes.KEY_RIGHT:
|
||||
case KeyCodes.KEY_SHIFT:
|
||||
case KeyCodes.KEY_TAB:
|
||||
case KeyCodes.KEY_UP:
|
||||
// Allow these, even if one of their assigned codes is
|
||||
// identical to an ASCII character we do not want to
|
||||
// allow in the box.
|
||||
//
|
||||
// We still want to let the user move around the input box
|
||||
// with their arrow keys, or to move between fields using tab.
|
||||
// Invalid characters introduced will be caught through the
|
||||
// server's own validation of the input data.
|
||||
//
|
||||
break;
|
||||
|
||||
default:
|
||||
final TextBox box = (TextBox) event.getSource();
|
||||
final String re;
|
||||
if (box.getCursorPos() == 0)
|
||||
re = Account.SSH_USER_NAME_PATTERN_FIRST;
|
||||
else
|
||||
re = Account.SSH_USER_NAME_PATTERN_REST;
|
||||
if (!String.valueOf(code).matches("^" + re + "$")) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SshKeyTable extends FancyFlexTable<AccountSshKey> {
|
||||
private static final String S_INVALID = "gerrit-SshKeyPanel-Invalid";
|
||||
|
||||
|
||||
@@ -59,6 +59,13 @@ public final class Account {
|
||||
FULL_NAME, SSH_USER_NAME, REGISTER_NEW_EMAIL;
|
||||
}
|
||||
|
||||
public static final String SSH_USER_NAME_PATTERN_FIRST = "[a-zA-Z]";
|
||||
public static final String SSH_USER_NAME_PATTERN_REST = "[a-zA-Z0-9._-]";
|
||||
|
||||
/** Regular expression that {@link #sshUserName} must match. */
|
||||
public static final String SSH_USER_NAME_PATTERN =
|
||||
"^" + SSH_USER_NAME_PATTERN_FIRST + SSH_USER_NAME_PATTERN_REST + "+$";
|
||||
|
||||
/** Key local to Gerrit to identify a user. */
|
||||
public static class Id extends IntKey<com.google.gwtorm.client.Key<?>> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
@@ -106,10 +113,6 @@ public final class Account {
|
||||
@Column(notNull = false)
|
||||
protected String preferredEmail;
|
||||
|
||||
/** Regular expression that {@link #sshUserName} must match (not enforced in this class). */
|
||||
public static final String SSH_USER_NAME_PATTERN =
|
||||
"^[a-zA-Z][a-zA-Z0-9._-]+$";
|
||||
|
||||
/** Username to authenticate as through SSH connections. */
|
||||
@Column(notNull = false)
|
||||
protected String sshUserName;
|
||||
|
||||
Reference in New Issue
Block a user