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:
Shawn O. Pearce
2009-09-22 12:30:11 -07:00
parent e411010517
commit 0bf520e74d
4 changed files with 77 additions and 6 deletions

View File

@@ -47,6 +47,7 @@ public interface AccountConstants extends Constants {
String sshUserName();
String buttonChangeSshUserName();
String invalidSshUserName();
String sshKeyInvalid();
String sshKeyAlgorithm();

View File

@@ -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

View File

@@ -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";

View File

@@ -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;