Performing handshake and online init in LoginDialog

This commit is contained in:
Kai S. K. Engelbart 2020-02-12 17:31:20 +01:00
parent 17eeed0bfb
commit 8714c8fe0e
3 changed files with 205 additions and 180 deletions

View File

@ -51,7 +51,7 @@ public class Receiver implements Runnable {
try (ObjectInputStream oin = new ObjectInputStream(new ByteArrayInputStream(objBytes))) { try (ObjectInputStream oin = new ObjectInputStream(new ByteArrayInputStream(objBytes))) {
Object obj = oin.readObject(); Object obj = oin.readObject();
logger.finest("Received object " + obj); logger.info("Received object " + obj);
// Get appropriate processor // Get appropriate processor
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")

View File

@ -2,17 +2,25 @@ package envoy.client.ui;
import java.awt.*; import java.awt.*;
import java.awt.event.ItemEvent; import java.awt.event.ItemEvent;
import java.io.IOException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.Arrays; import java.util.Arrays;
import java.util.logging.Logger;
import javax.naming.TimeLimitExceededException;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.EmptyBorder; import javax.swing.border.EmptyBorder;
import envoy.client.data.Settings; import envoy.client.data.*;
import envoy.client.event.HandshakeSuccessfulEvent; import envoy.client.event.HandshakeSuccessfulEvent;
import envoy.client.net.Client;
import envoy.client.util.EnvoyLog;
import envoy.data.LoginCredentials; import envoy.data.LoginCredentials;
import envoy.data.Message;
import envoy.data.User;
import envoy.event.EventBus; import envoy.event.EventBus;
import envoy.event.HandshakeRejectionEvent; import envoy.event.HandshakeRejectionEvent;
import envoy.exception.EnvoyException;
/** /**
* Project: <strong>envoy-client</strong><br> * Project: <strong>envoy-client</strong><br>
@ -25,7 +33,7 @@ import envoy.event.HandshakeRejectionEvent;
*/ */
public class LoginDialog extends JDialog { public class LoginDialog extends JDialog {
private final JPanel contentPanel; private JPanel contentPanel;
private JTextField textField; private JTextField textField;
private JPasswordField passwordField; private JPasswordField passwordField;
private JPasswordField repeatPasswordField; private JPasswordField repeatPasswordField;
@ -47,14 +55,111 @@ public class LoginDialog extends JDialog {
private LoginCredentials credentials; private LoginCredentials credentials;
private final Client client;
private final LocalDb localDb;
private final Cache<Message> receivedMessageCache;
private static final Config config = Config.getInstance();
private static final Logger logger = EnvoyLog.getLogger(LoginDialog.class.getSimpleName());
private static final long serialVersionUID = 352021600833907468L; private static final long serialVersionUID = 352021600833907468L;
/** /**
* Displays a dialog enabling the user to enter their user name and password. * Displays a dialog enabling the user to enter their user name and password.
* *
* @param client the client used to perform the handshake
* @param localDb the local database in which data is persisted
* @param receivedMessageCache the cache that stored messages received during
* the handshake
* @since Envoy v0.3-alpha * @since Envoy v0.3-alpha
*/ */
public LoginDialog() { public LoginDialog(Client client, LocalDb localDb, Cache<Message> receivedMessageCache) {
this.client = client;
this.localDb = localDb;
this.receivedMessageCache = receivedMessageCache;
// Prepare handshake
localDb.loadIdGenerator();
initUi();
okButton.addActionListener((evt) -> {
try {
if (registerCheckBox.isSelected()) {
// Check password equality
if (Arrays.equals(passwordField.getPassword(), repeatPasswordField.getPassword()))
credentials = new LoginCredentials(textField.getText(), passwordField.getPassword(), true);
else {
JOptionPane.showMessageDialog(this, "The repeated password is not the origional password!");
clearPasswordFields();
}
} else credentials = new LoginCredentials(textField.getText(), passwordField.getPassword(), false);
performHandshake();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
});
// Listen to handshake rejections
EventBus.getInstance().register(HandshakeRejectionEvent.class, evt -> {
for (Component c : contentPanel.getComponents())
if (c == errorMessage) contentPanel.remove(errorMessage);
clearPasswordFields();
errorMessage = new JLabel(evt.get());
gbc_errorMessage = new GridBagConstraints();
gbc_errorMessage.gridx = 2;
gbc_errorMessage.gridy = 0;
gbc_errorMessage.fill = GridBagConstraints.HORIZONTAL;
gbc_errorMessage.insets = new Insets(5, 5, 5, 5);
contentPanel.add(errorMessage, gbc_errorMessage);
contentPanel.revalidate();
contentPanel.repaint();
});
// Exit the application when the dialog is cancelled
cancelButton.addActionListener(evt -> { logger.info("The login process has been cancelled. Exiting..."); System.exit(0); });
// Log in directly if configured
if (config.hasLoginCredentials()) {
credentials = config.getLoginCredentials();
performHandshake();
return;
}
setVisible(true);
}
private void performHandshake() {
try {
client.performHandshake(credentials, receivedMessageCache);
if (client.isOnline()) {
client.initReceiver(localDb, receivedMessageCache);
dispose();
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
} catch (TimeLimitExceededException e) {
logger.warning("Could not connect to server. Trying offline mode...");
e.printStackTrace();
try {
// Try entering offline mode
localDb.loadUsers();
User clientUser = localDb.getUsers().get(credentials.getIdentifier());
if (clientUser == null) throw new EnvoyException("Could not enter offline mode: user name unknown");
client.setSender(clientUser);
JOptionPane.showMessageDialog(null,
"A connection to the server could not be established. Starting in offline mode.\n" + e,
"Connection error",
JOptionPane.WARNING_MESSAGE);
dispose();
} catch (Exception e1) {
JOptionPane.showMessageDialog(null, e1, "Client error", JOptionPane.ERROR_MESSAGE);
System.exit(1);
return;
}
}
}
private void initUi() {
setSize(338, 123); setSize(338, 123);
setLocationRelativeTo(null); setLocationRelativeTo(null);
getContentPane().setLayout(new BorderLayout()); getContentPane().setLayout(new BorderLayout());
@ -67,7 +172,7 @@ public class LoginDialog extends JDialog {
gbl_contentPanel.columnWeights = new double[] { 0.0, 1.0, Double.MIN_VALUE }; gbl_contentPanel.columnWeights = new double[] { 0.0, 1.0, Double.MIN_VALUE };
gbl_contentPanel.rowWeights = new double[] { 0.0, 0.0, Double.MIN_VALUE }; gbl_contentPanel.rowWeights = new double[] { 0.0, 0.0, Double.MIN_VALUE };
contentPanel.setLayout(gbl_contentPanel); contentPanel.setLayout(gbl_contentPanel);
{
lblUserName = new JLabel("Username:"); lblUserName = new JLabel("Username:");
GridBagConstraints gbc_lblUserName = new GridBagConstraints(); GridBagConstraints gbc_lblUserName = new GridBagConstraints();
gbc_lblUserName.anchor = GridBagConstraints.EAST; gbc_lblUserName.anchor = GridBagConstraints.EAST;
@ -75,8 +180,7 @@ public class LoginDialog extends JDialog {
gbc_lblUserName.gridx = 0; gbc_lblUserName.gridx = 0;
gbc_lblUserName.gridy = 0; gbc_lblUserName.gridy = 0;
contentPanel.add(lblUserName, gbc_lblUserName); contentPanel.add(lblUserName, gbc_lblUserName);
}
{
textField = new JTextField(); textField = new JTextField();
textField.setBorder(null); textField.setBorder(null);
GridBagConstraints gbc_textField = new GridBagConstraints(); GridBagConstraints gbc_textField = new GridBagConstraints();
@ -86,8 +190,7 @@ public class LoginDialog extends JDialog {
gbc_textField.gridy = 0; gbc_textField.gridy = 0;
contentPanel.add(textField, gbc_textField); contentPanel.add(textField, gbc_textField);
textField.setColumns(10); textField.setColumns(10);
}
{
lblPassword = new JLabel("Password:"); lblPassword = new JLabel("Password:");
GridBagConstraints gbc_lblPassword = new GridBagConstraints(); GridBagConstraints gbc_lblPassword = new GridBagConstraints();
gbc_lblPassword.anchor = GridBagConstraints.EAST; gbc_lblPassword.anchor = GridBagConstraints.EAST;
@ -95,8 +198,7 @@ public class LoginDialog extends JDialog {
gbc_lblPassword.gridx = 0; gbc_lblPassword.gridx = 0;
gbc_lblPassword.gridy = 1; gbc_lblPassword.gridy = 1;
contentPanel.add(lblPassword, gbc_lblPassword); contentPanel.add(lblPassword, gbc_lblPassword);
}
{
passwordField = new JPasswordField(); passwordField = new JPasswordField();
passwordField.setBorder(null); passwordField.setBorder(null);
GridBagConstraints gbc_passwordField = new GridBagConstraints(); GridBagConstraints gbc_passwordField = new GridBagConstraints();
@ -104,38 +206,20 @@ public class LoginDialog extends JDialog {
gbc_passwordField.gridx = 1; gbc_passwordField.gridx = 1;
gbc_passwordField.gridy = 1; gbc_passwordField.gridy = 1;
contentPanel.add(passwordField, gbc_passwordField); contentPanel.add(passwordField, gbc_passwordField);
}
{
lblRepeatPassword = new JLabel("Repeat Password:"); lblRepeatPassword = new JLabel("Repeat Password:");
gbc_lblRepeatPassword = new GridBagConstraints(); gbc_lblRepeatPassword = new GridBagConstraints();
gbc_lblRepeatPassword.anchor = GridBagConstraints.EAST; gbc_lblRepeatPassword.anchor = GridBagConstraints.EAST;
gbc_lblRepeatPassword.insets = new Insets(0, 0, 0, 5); gbc_lblRepeatPassword.insets = new Insets(0, 0, 0, 5);
gbc_lblRepeatPassword.gridx = 0; gbc_lblRepeatPassword.gridx = 0;
gbc_lblRepeatPassword.gridy = 2; gbc_lblRepeatPassword.gridy = 2;
}
{
repeatPasswordField = new JPasswordField(); repeatPasswordField = new JPasswordField();
gbc_repeatPasswordField = new GridBagConstraints(); gbc_repeatPasswordField = new GridBagConstraints();
gbc_repeatPasswordField.fill = GridBagConstraints.HORIZONTAL; gbc_repeatPasswordField.fill = GridBagConstraints.HORIZONTAL;
gbc_repeatPasswordField.gridx = 1; gbc_repeatPasswordField.gridx = 1;
gbc_repeatPasswordField.gridy = 2; gbc_repeatPasswordField.gridy = 2;
}
{
EventBus.getInstance().register(HandshakeRejectionEvent.class, evt -> {
contentPanel.remove(errorMessage);
clearPasswordFields();
// TODO delete - only for testing purposes
System.out.println("Caught HandshakeRejectionEvent with reason" + evt.get());
errorMessage = new JLabel(evt.get());
gbc_errorMessage = new GridBagConstraints();
gbc_errorMessage.gridx = 2;
gbc_errorMessage.gridy = 0;
gbc_errorMessage.fill = GridBagConstraints.HORIZONTAL;
gbc_errorMessage.insets = new Insets(5, 5, 5, 5);
contentPanel.add(errorMessage, gbc_errorMessage);
});
}
{
buttonPane = new JPanel(); buttonPane = new JPanel();
registerText = new JTextPane(); registerText = new JTextPane();
@ -170,47 +254,27 @@ public class LoginDialog extends JDialog {
buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT)); buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT));
getContentPane().add(buttonPane, BorderLayout.SOUTH); getContentPane().add(buttonPane, BorderLayout.SOUTH);
{
okButton = new PrimaryButton("OK"); okButton = new PrimaryButton("OK");
okButton.addActionListener((evt) -> {
try {
if (registerCheckBox.isSelected()) {
// password checking
if (Arrays.equals(passwordField.getPassword(), repeatPasswordField.getPassword()))
credentials = new LoginCredentials(textField.getText(), passwordField.getPassword(), true);
else {
JOptionPane.showMessageDialog(this, "The repeated password is not the origional password!");
clearPasswordFields();
}
} else credentials = new LoginCredentials(textField.getText(), passwordField.getPassword(), false);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
});
okButton.setActionCommand("OK"); okButton.setActionCommand("OK");
buttonPane.add(okButton); buttonPane.add(okButton);
getRootPane().setDefaultButton(okButton); getRootPane().setDefaultButton(okButton);
}
{
cancelButton = new PrimaryButton("Cancel"); cancelButton = new PrimaryButton("Cancel");
cancelButton.addActionListener((evt) -> dispose());
cancelButton.setActionCommand("Cancel"); cancelButton.setActionCommand("Cancel");
buttonPane.add(cancelButton); buttonPane.add(cancelButton);
}
}
setTheme(); setTheme();
setModal(true); setModalityType(Dialog.DEFAULT_MODALITY_TYPE);
setVisible(true);
EventBus.getInstance().register(HandshakeSuccessfulEvent.class, evt -> dispose()); EventBus.getInstance().register(HandshakeSuccessfulEvent.class, evt -> dispose());
} }
/** /**
* Resets the text stored in the passwort fields. * Resets the text stored in the password fields.
* *
* @since Envoy v0.3-alpha * @since Envoy v0.3-alpha
*/ */
public void clearPasswordFields() { private void clearPasswordFields() {
passwordField.setText(null); passwordField.setText(null);
repeatPasswordField.setText(null); repeatPasswordField.setText(null);
} }
@ -258,11 +322,4 @@ public class LoginDialog extends JDialog {
cancelButton.setBackground(theme.getInteractableBackgroundColor()); cancelButton.setBackground(theme.getInteractableBackgroundColor());
cancelButton.setForeground(theme.getInteractableForegroundColor()); cancelButton.setForeground(theme.getInteractableForegroundColor());
} }
/**
* @return the {@link LoginCredentials} entered by the user, or {@code null} if
* the dialog has been cancelled
* @since Envoy v0.3-alpha
*/
public LoginCredentials getCredentials() { return credentials; }
} }

View File

@ -15,9 +15,7 @@ import envoy.client.data.*;
import envoy.client.net.Client; import envoy.client.net.Client;
import envoy.client.net.WriteProxy; import envoy.client.net.WriteProxy;
import envoy.client.util.EnvoyLog; import envoy.client.util.EnvoyLog;
import envoy.data.LoginCredentials;
import envoy.data.Message; import envoy.data.Message;
import envoy.data.User;
import envoy.exception.EnvoyException; import envoy.exception.EnvoyException;
/** /**
@ -74,14 +72,6 @@ public class Startup {
EnvoyLog.setFileLevelBarrier(config.getFileLevelBarrier()); EnvoyLog.setFileLevelBarrier(config.getFileLevelBarrier());
EnvoyLog.setConsoleLevelBarrier(config.getConsoleLevelBarrier()); EnvoyLog.setConsoleLevelBarrier(config.getConsoleLevelBarrier());
// Acquire login credentials
LoginCredentials credentials = config.hasLoginCredentials() ? config.getLoginCredentials() : new LoginDialog().getCredentials();
if (credentials == null) {
logger.info("The login process has been cancelled. Exiting...");
System.exit(0);
}
// Initialize the local database // Initialize the local database
LocalDb localDb; LocalDb localDb;
if (config.isIgnoreLocalDB()) { if (config.isIgnoreLocalDB()) {
@ -95,45 +85,23 @@ public class Startup {
} catch (IOException e3) { } catch (IOException e3) {
logger.log(Level.SEVERE, "Could not initialize local database", e3); logger.log(Level.SEVERE, "Could not initialize local database", e3);
JOptionPane JOptionPane
.showMessageDialog(null, "Could not initialize local database!\n" + e3.toString(), "Local database error", JOptionPane.ERROR_MESSAGE); .showMessageDialog(null, "Could not initialize local database!\n" + e3, "Local database error", JOptionPane.ERROR_MESSAGE);
System.exit(1); System.exit(1);
return; return;
} }
SwingUtilities.invokeLater(() -> chatWindow.setVisible(true)); // Initialize client and unread message cache
// Acquire the client user (with ID) either from the server or from the local
// database, which triggers offline mode
Client client = new Client(); Client client = new Client();
Cache<Message> cache = new Cache<>(); Cache<Message> cache = new Cache<>();
try {
// Try entering online mode first // Try to connect to the server
localDb.loadIdGenerator(); new LoginDialog(client, localDb, cache);
client.performHandshake(credentials, cache); SwingUtilities.invokeLater(() -> chatWindow.setVisible(true));
client.initReceiver(localDb, cache);
} catch (Exception e1) {
logger.warning("Could not connect to server. Trying offline mode...");
e1.printStackTrace();
try {
// Try entering offline mode
localDb.loadUsers();
User clientUser = localDb.getUsers().get(credentials.getIdentifier());
if (clientUser == null) throw new EnvoyException("Could not enter offline mode: user name unknown");
client.setSender(clientUser);
JOptionPane.showMessageDialog(null,
"A connection to the server could not be established. Starting in offline mode.\n" + e1,
"Connection error",
JOptionPane.WARNING_MESSAGE);
} catch (Exception e2) {
JOptionPane.showMessageDialog(null, e2.toString(), "Client error", JOptionPane.ERROR_MESSAGE);
System.exit(1);
return;
}
}
// Set client user in local database // Set client user in local database
localDb.setUser(client.getSender()); localDb.setUser(client.getSender());
// Initialize chats in local database // Initialize chats in local database
try { try {
localDb.initializeUserStorage(); localDb.initializeUserStorage();