From 1f5d5b00543c21763aec50c1170b17bf70a296dc Mon Sep 17 00:00:00 2001 From: delvh Date: Sat, 1 Aug 2020 09:43:15 +0200 Subject: [PATCH 1/8] Changed SettingsPane mechanism a bit --- .../main/java/envoy/client/net/Client.java | 2 +- .../envoy/client/ui/controller/ChatScene.java | 27 ++++---- .../client/ui/controller/SettingsScene.java | 12 ++-- .../ui/settings/DownloadSettingsPane.java | 4 +- .../ui/settings/GeneralSettingsPane.java | 4 -- .../client/ui/settings/SettingsPane.java | 9 ++- .../client/ui/settings/UserSettingsPane.java | 68 +++++++++++++++++++ 7 files changed, 96 insertions(+), 30 deletions(-) create mode 100644 client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java diff --git a/client/src/main/java/envoy/client/net/Client.java b/client/src/main/java/envoy/client/net/Client.java index b642141..c395045 100644 --- a/client/src/main/java/envoy/client/net/Client.java +++ b/client/src/main/java/envoy/client/net/Client.java @@ -193,7 +193,7 @@ public class Client implements Closeable { * @param evt the event to send * @throws IOException if the event did not reach the server */ - public void sendEvent(Event evt) throws IOException { writeObject(evt); } + public void sendEvent(Event evt) throws IOException { if (online) writeObject(evt); } /** * Requests a new {@link IDGenerator} from the server. diff --git a/client/src/main/java/envoy/client/ui/controller/ChatScene.java b/client/src/main/java/envoy/client/ui/controller/ChatScene.java index a708dd0..9af7c62 100644 --- a/client/src/main/java/envoy/client/ui/controller/ChatScene.java +++ b/client/src/main/java/envoy/client/ui/controller/ChatScene.java @@ -147,7 +147,7 @@ public final class ChatScene implements Restorable { // The sender of the message is the recipient of the chat // Exceptions: this user is the sender (sync) or group message (group is // recipient) - final long recipientID = message instanceof GroupMessage || message.getSenderID() == localDB.getUser().getID() ? message.getRecipientID() + final var recipientID = message instanceof GroupMessage || message.getSenderID() == localDB.getUser().getID() ? message.getRecipientID() : message.getSenderID(); localDB.getChat(recipientID).ifPresent(chat -> { chat.insert(message); @@ -200,7 +200,7 @@ public final class ChatScene implements Restorable { switch (e.getOperationType()) { case ADD: if (contact instanceof User) localDB.getUsers().put(contact.getName(), (User) contact); - final Chat chat = contact instanceof User ? new Chat(contact) : new GroupChat(client.getSender(), contact); + final var chat = contact instanceof User ? new Chat(contact) : new GroupChat(client.getSender(), contact); Platform.runLater(() -> chatList.getItems().add(chat)); break; case REMOVE: @@ -264,7 +264,7 @@ public final class ChatScene implements Restorable { private void chatListClicked() { if (chatList.getSelectionModel().isEmpty()) return; - final Contact user = chatList.getSelectionModel().getSelectedItem().getRecipient(); + final var user = chatList.getSelectionModel().getSelectedItem().getRecipient(); if (user != null && (currentChat == null || !user.equals(currentChat.getRecipient()))) { // LEON: JFC <===> JAVA FRIED CHICKEN <=/=> Java Foundation Classes @@ -311,7 +311,7 @@ public final class ChatScene implements Restorable { @FXML private void settingsButtonClicked() { sceneContext.load(SceneContext.SceneInfo.SETTINGS_SCENE); - sceneContext.getController().initializeData(sceneContext); + sceneContext.getController().initializeData(sceneContext, localDB.getUser()); } /** @@ -377,7 +377,7 @@ public final class ChatScene implements Restorable { } // Get attachment type (default is document) - AttachmentType type = AttachmentType.DOCUMENT; + var type = AttachmentType.DOCUMENT; switch (fileChooser.getSelectedExtensionFilter().getDescription()) { case "Pictures": type = AttachmentType.PICTURE; @@ -452,7 +452,7 @@ public final class ChatScene implements Restorable { messageTextUpdated(); // Sending an IsTyping event if none has been sent for // IsTyping#millisecondsActive - if (client.isOnline() && currentChat.getLastWritingEvent() + IsTyping.millisecondsActive <= System.currentTimeMillis()) { + if (currentChat.getLastWritingEvent() + IsTyping.millisecondsActive <= System.currentTimeMillis()) { eventBus.dispatch(new SendEvent(new IsTyping(getChatID(), currentChat.getRecipient().getID()))); currentChat.lastWritingEventWasNow(); } @@ -461,9 +461,9 @@ public final class ChatScene implements Restorable { } /** - * Returns the id that should be used to send things to the server: - * the id of 'our' {@link User} if the recipient of that object is another User, - * else the id of the {@link Group} 'our' user is sending to. + * Returns the id that should be used to send things to the server: the id of + * 'our' {@link User} if the recipient of that object is another User, else the + * id of the {@link Group} 'our' user is sending to. * * @return an id that can be sent to the server * @since Envoy Client v0.2-beta @@ -517,8 +517,8 @@ public final class ChatScene implements Restorable { * @since Envoy Client v0.1-beta */ private void updateRemainingCharsLabel() { - final int currentLength = messageTextArea.getText().length(); - final int remainingLength = MAX_MESSAGE_LENGTH - currentLength; + final var currentLength = messageTextArea.getText().length(); + final var remainingLength = MAX_MESSAGE_LENGTH - currentLength; remainingChars.setText(String.format("remaining chars: %d/%d", remainingLength, MAX_MESSAGE_LENGTH)); remainingChars.setTextFill(Color.rgb(currentLength, remainingLength, 0, 1)); } @@ -607,9 +607,8 @@ public final class ChatScene implements Restorable { /** * Updates the {@code attachmentView} in terms of visibility.
- * Additionally resets the shown image to - * {@code DEFAULT_ATTACHMENT_VIEW_IMAGE} if another image is currently - * present. + * Additionally resets the shown image to {@code DEFAULT_ATTACHMENT_VIEW_IMAGE} + * if another image is currently present. * * @param visible whether the {@code attachmentView} should be displayed * @since Envoy Client v0.1-beta diff --git a/client/src/main/java/envoy/client/ui/controller/SettingsScene.java b/client/src/main/java/envoy/client/ui/controller/SettingsScene.java index f3358a9..7af8659 100644 --- a/client/src/main/java/envoy/client/ui/controller/SettingsScene.java +++ b/client/src/main/java/envoy/client/ui/controller/SettingsScene.java @@ -4,9 +4,8 @@ import javafx.fxml.FXML; import javafx.scene.control.*; import envoy.client.ui.SceneContext; -import envoy.client.ui.settings.DownloadSettingsPane; -import envoy.client.ui.settings.GeneralSettingsPane; -import envoy.client.ui.settings.SettingsPane; +import envoy.client.ui.settings.*; +import envoy.data.User; /** * Project: envoy-client
@@ -28,10 +27,13 @@ public class SettingsScene { /** * @param sceneContext enables the user to return to the chat scene + * @param client the user who uses Envoy * @since Envoy Client v0.1-beta */ - public void initializeData(SceneContext sceneContext) { + public void initializeData(SceneContext sceneContext, User client) { this.sceneContext = sceneContext; + settingsList.getItems().add(new GeneralSettingsPane()); + settingsList.getItems().add(new UserSettingsPane(sceneContext, client)); settingsList.getItems().add(new DownloadSettingsPane(sceneContext)); } @@ -45,8 +47,6 @@ public class SettingsScene { if (!empty && item != null) setGraphic(new Label(item.getTitle())); } }); - - settingsList.getItems().add(new GeneralSettingsPane()); } @FXML diff --git a/client/src/main/java/envoy/client/ui/settings/DownloadSettingsPane.java b/client/src/main/java/envoy/client/ui/settings/DownloadSettingsPane.java index bdc1352..90e06f0 100644 --- a/client/src/main/java/envoy/client/ui/settings/DownloadSettingsPane.java +++ b/client/src/main/java/envoy/client/ui/settings/DownloadSettingsPane.java @@ -5,7 +5,6 @@ import javafx.scene.control.Button; import javafx.scene.control.CheckBox; import javafx.scene.control.Label; import javafx.scene.layout.HBox; -import javafx.scene.layout.VBox; import javafx.stage.DirectoryChooser; import envoy.client.ui.SceneContext; @@ -31,7 +30,7 @@ public class DownloadSettingsPane extends SettingsPane { */ public DownloadSettingsPane(SceneContext sceneContext) { super("Download"); - final var vbox = new VBox(15); + vbox.setSpacing(15); vbox.setPadding(new Insets(15)); // checkbox to disable asking final var checkBox = new CheckBox(settings.getItems().get("autoSaveDownloads").getUserFriendlyName()); @@ -60,6 +59,5 @@ public class DownloadSettingsPane extends SettingsPane { }); hbox.getChildren().add(button); vbox.getChildren().add(hbox); - getChildren().add(vbox); } } diff --git a/client/src/main/java/envoy/client/ui/settings/GeneralSettingsPane.java b/client/src/main/java/envoy/client/ui/settings/GeneralSettingsPane.java index af46fbb..20189f4 100644 --- a/client/src/main/java/envoy/client/ui/settings/GeneralSettingsPane.java +++ b/client/src/main/java/envoy/client/ui/settings/GeneralSettingsPane.java @@ -3,7 +3,6 @@ package envoy.client.ui.settings; import java.util.List; import javafx.scene.control.ComboBox; -import javafx.scene.layout.VBox; import envoy.client.data.SettingsItem; import envoy.client.event.ThemeChangeEvent; @@ -25,7 +24,6 @@ public class GeneralSettingsPane extends SettingsPane { */ public GeneralSettingsPane() { super("General"); - final var vbox = new VBox(); // TODO: Support other value types List.of("hideOnClose", "enterToSend") @@ -48,7 +46,5 @@ public class GeneralSettingsPane extends SettingsPane { // TODO add action when value is changed statusComboBox.setOnAction(e -> {}); vbox.getChildren().add(statusComboBox); - - getChildren().add(vbox); } } diff --git a/client/src/main/java/envoy/client/ui/settings/SettingsPane.java b/client/src/main/java/envoy/client/ui/settings/SettingsPane.java index 49f8abd..e7b884d 100644 --- a/client/src/main/java/envoy/client/ui/settings/SettingsPane.java +++ b/client/src/main/java/envoy/client/ui/settings/SettingsPane.java @@ -1,6 +1,7 @@ package envoy.client.ui.settings; import javafx.scene.layout.Pane; +import javafx.scene.layout.VBox; import envoy.client.data.Settings; @@ -14,11 +15,15 @@ import envoy.client.data.Settings; */ public abstract class SettingsPane extends Pane { - protected String title; + protected String title; + protected final VBox vbox = new VBox(); protected static final Settings settings = Settings.getInstance(); - protected SettingsPane(String title) { this.title = title; } + protected SettingsPane(String title) { + this.title = title; + getChildren().add(vbox); + } /** * @return the title of this settings pane diff --git a/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java b/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java new file mode 100644 index 0000000..becc699 --- /dev/null +++ b/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java @@ -0,0 +1,68 @@ +package envoy.client.ui.settings; + +import javafx.scene.control.Alert; +import javafx.scene.control.Alert.AlertType; + +import envoy.client.event.SendEvent; +import envoy.client.ui.SceneContext; +import envoy.data.User; +import envoy.event.*; +import envoy.util.Bounds; + +/** + * Project: envoy-client
+ * File: UserSettingsPane.java
+ * Created: 31.07.2020
+ * + * @author Leon Hofmeister + * @since Envoy Client v0.2-beta + */ +public class UserSettingsPane extends SettingsPane { + + private boolean profilePicChanged, usernameChanged, passwordChanged, validPassword; + private byte[] currentImageBytes; + private String newUsername, newPassword; + + /** + * Creates a new {@code UserSettingsPane}. + * + * @param sceneContext the {@code SceneContext} to block input to Envoy + * @param user the user who wants to customize his profile + * @since Envoy Client v0.2-beta + */ + public UserSettingsPane(SceneContext sceneContext, User user) { super("User"); } + + /** + * Saves the given input and sends the changed input to the server + * + * @param username the new username + * @since Envoy Client v0.2-beta + */ + private void save(long userID, String oldPassword) { + final var eventbus = EventBus.getInstance(); + + // The profile pic was changed + if (profilePicChanged) eventbus.dispatch(new SendEvent(new ProfilePicChange(currentImageBytes, userID))); + + // The username was changed + final var validContactName = Bounds.isValidContactName(newUsername); + if (usernameChanged && validContactName) eventbus.dispatch(new SendEvent(new NameChange(userID, newUsername))); + else if (!validContactName) { + final var alert = new Alert(AlertType.ERROR); + alert.setTitle("Invalid username"); + alert.setContentText("The entered username does not conform with the naming limitations: " + Bounds.CONTACT_NAME_PATTERN); + alert.showAndWait(); + return; + } + + // The password was changed + if (passwordChanged && validPassword) eventbus.dispatch(new SendEvent(new PasswordChangeRequest(newPassword, oldPassword, userID))); + else if (!(validPassword || newPassword != null && newPassword.isBlank())) { + final var alert = new Alert(AlertType.ERROR); + alert.setTitle("Unequal Password"); + alert.setContentText("Repeated password is unequal to the chosen new password"); + alert.showAndWait(); + return; + } + } +} From 2af098fb280a3ba0e4c0810b8acbe6cc52ccbc16 Mon Sep 17 00:00:00 2001 From: delvh Date: Sat, 1 Aug 2020 09:54:18 +0200 Subject: [PATCH 2/8] Added ability to change the user name on the client side --- .../client/ui/settings/UserSettingsPane.java | 21 ++++++++++++++++++- .../src/main/java/envoy/server/Startup.java | 3 ++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java b/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java index becc699..993b6bf 100644 --- a/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java +++ b/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java @@ -1,7 +1,11 @@ package envoy.client.ui.settings; +import javafx.event.EventHandler; import javafx.scene.control.Alert; import javafx.scene.control.Alert.AlertType; +import javafx.scene.control.TextField; +import javafx.scene.input.InputEvent; +import javafx.scene.layout.HBox; import envoy.client.event.SendEvent; import envoy.client.ui.SceneContext; @@ -30,7 +34,22 @@ public class UserSettingsPane extends SettingsPane { * @param user the user who wants to customize his profile * @since Envoy Client v0.2-beta */ - public UserSettingsPane(SceneContext sceneContext, User user) { super("User"); } + public UserSettingsPane(SceneContext sceneContext, User user) { + super("User"); // Display of profile picture change mechanism + final var hbox = new HBox(); + + // Displaying the username change mechanism + final var username = user.getName(); + final var usernameTextField = new TextField(username); + final EventHandler textChanged = e -> { + newUsername = usernameTextField.getText(); + usernameChanged = newUsername != username; + }; + usernameTextField.setOnInputMethodTextChanged(textChanged); + usernameTextField.setOnKeyTyped(textChanged); + hbox.getChildren().add(usernameTextField); + vbox.getChildren().add(hbox); + } /** * Saves the given input and sends the changed input to the server diff --git a/server/src/main/java/envoy/server/Startup.java b/server/src/main/java/envoy/server/Startup.java index ac24c40..bd00a66 100755 --- a/server/src/main/java/envoy/server/Startup.java +++ b/server/src/main/java/envoy/server/Startup.java @@ -70,7 +70,8 @@ public class Startup { new IDGeneratorRequestProcessor(), new UserSearchProcessor(), new ContactOperationProcessor(), - new IsTypingProcessor()))); + new IsTypingProcessor(), + new NameChangeProcessor()))); // Initialize the current message ID final PersistenceManager persistenceManager = PersistenceManager.getInstance(); From fc63ea0a46982019d6d53b4272920cd2af918f49 Mon Sep 17 00:00:00 2001 From: delvh Date: Sat, 1 Aug 2020 10:00:29 +0200 Subject: [PATCH 3/8] Added profile pic change mechanism on client and common side --- .../client/ui/settings/UserSettingsPane.java | 44 ++++++++++++++++++- .../java/envoy/event/ProfilePicChange.java | 33 ++++++++++++++ .../processors/ProfilePicChangeProcessor.java | 20 +++++++++ 3 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 common/src/main/java/envoy/event/ProfilePicChange.java create mode 100644 server/src/main/java/envoy/server/processors/ProfilePicChangeProcessor.java diff --git a/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java b/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java index 993b6bf..2ea37f4 100644 --- a/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java +++ b/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java @@ -1,13 +1,23 @@ package envoy.client.ui.settings; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + import javafx.event.EventHandler; +import javafx.scene.Cursor; import javafx.scene.control.Alert; import javafx.scene.control.Alert.AlertType; import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; import javafx.scene.input.InputEvent; import javafx.scene.layout.HBox; +import javafx.stage.FileChooser; import envoy.client.event.SendEvent; +import envoy.client.ui.IconUtil; import envoy.client.ui.SceneContext; import envoy.data.User; import envoy.event.*; @@ -35,8 +45,40 @@ public class UserSettingsPane extends SettingsPane { * @since Envoy Client v0.2-beta */ public UserSettingsPane(SceneContext sceneContext, User user) { - super("User"); // Display of profile picture change mechanism + super("User"); + + // Display of profile pic change mechanism final var hbox = new HBox(); + // TODO: display current profile pic + final var profilePic = new ImageView(IconUtil.loadIcon("envoy_logo", 50)); + profilePic.setCursor(Cursor.HAND); + profilePic.setOnMouseClicked(e -> { + final var pictureChooser = new FileChooser(); + + pictureChooser.setTitle("Select a new picture"); + pictureChooser.setInitialDirectory(new File(System.getProperty("user.home"))); + pictureChooser.setSelectedExtensionFilter(new FileChooser.ExtensionFilter("Pictures", "*.png", "*.jpg", "*.bmp", "*.gif")); + + final var file = pictureChooser.showOpenDialog(sceneContext.getStage()); + + if (file != null) { + + // Check max file size + if (file.length() > 5E6) { + new Alert(AlertType.WARNING, "The selected file exceeds the size limit of 5MB!").showAndWait(); + return; + } + + try { + currentImageBytes = Files.readAllBytes(file.toPath()); + profilePic.setImage(new Image(new ByteArrayInputStream(currentImageBytes))); + profilePicChanged = true; + } catch (final IOException e1) { + e1.printStackTrace(); + } + } + }); + hbox.getChildren().add(profilePic); // Displaying the username change mechanism final var username = user.getName(); diff --git a/common/src/main/java/envoy/event/ProfilePicChange.java b/common/src/main/java/envoy/event/ProfilePicChange.java new file mode 100644 index 0000000..52c5941 --- /dev/null +++ b/common/src/main/java/envoy/event/ProfilePicChange.java @@ -0,0 +1,33 @@ +package envoy.event; + +/** + * Project: envoy-common
+ * File: ProfilePicChange.java
+ * Created: 31.07.2020
+ * + * @author Leon Hofmeister + * @since Envoy Common v0.2-beta + */ +public class ProfilePicChange extends Event { + + private final long id; + + private static final long serialVersionUID = 0L; + + /** + * @param value the byte[] of the new image + * @param userID the ID of the user who changed his profile pic + * @since Envoy Common v0.2-beta + */ + public ProfilePicChange(byte[] value, long userID) { + super(value); + id = userID; + } + + /** + * @return the id + * @since Envoy Common v0.2-beta + */ + public long getId() { return id; } + +} diff --git a/server/src/main/java/envoy/server/processors/ProfilePicChangeProcessor.java b/server/src/main/java/envoy/server/processors/ProfilePicChangeProcessor.java new file mode 100644 index 0000000..10069a8 --- /dev/null +++ b/server/src/main/java/envoy/server/processors/ProfilePicChangeProcessor.java @@ -0,0 +1,20 @@ +package envoy.server.processors; + +import java.io.IOException; + +import envoy.event.ProfilePicChange; +import envoy.server.net.ObjectWriteProxy; + +/** + * Project: envoy-server-standalone
+ * File: ProfilePicChangeProcessor.java
+ * Created: 01.08.2020
+ * + * @author Leon Hofmeister + * @since Envoy Server v0.2-beta + */ +public class ProfilePicChangeProcessor implements ObjectProcessor { + + @Override + public void process(ProfilePicChange event, long socketID, ObjectWriteProxy writeProxy) throws IOException {} +} From 41cd11f180c259c804d7409e533cc3f28cd9ee53 Mon Sep 17 00:00:00 2001 From: delvh Date: Sat, 1 Aug 2020 10:17:39 +0200 Subject: [PATCH 4/8] Added ability to change the password, theoretically on client and server (needs testing!) --- .../main/java/envoy/client/net/Client.java | 6 +++ .../client/ui/settings/UserSettingsPane.java | 44 ++++++++++++++++--- .../envoy/event/PasswordChangeRequest.java | 44 +++++++++++++++++++ .../envoy/event/PasswordChangeResult.java | 26 +++++++++++ .../PasswordChangeRequestProcessor.java | 29 ++++++++++++ 5 files changed, 144 insertions(+), 5 deletions(-) create mode 100644 common/src/main/java/envoy/event/PasswordChangeRequest.java create mode 100644 common/src/main/java/envoy/event/PasswordChangeResult.java create mode 100644 server/src/main/java/envoy/server/processors/PasswordChangeRequestProcessor.java diff --git a/client/src/main/java/envoy/client/net/Client.java b/client/src/main/java/envoy/client/net/Client.java index c395045..15e3c01 100644 --- a/client/src/main/java/envoy/client/net/Client.java +++ b/client/src/main/java/envoy/client/net/Client.java @@ -158,6 +158,12 @@ public class Client implements Closeable { // Process IsTyping events receiver.registerProcessor(IsTyping.class, eventBus::dispatch); + // Process PasswordChangeResults + receiver.registerProcessor(PasswordChangeResult.class, eventBus::dispatch); + + // Process ProfilePicChanges + receiver.registerProcessor(ProfilePicChange.class, eventBus::dispatch); + // Send event eventBus.register(SendEvent.class, evt -> { try { diff --git a/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java b/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java index 2ea37f4..1b5e2de 100644 --- a/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java +++ b/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java @@ -6,10 +6,10 @@ import java.io.IOException; import java.nio.file.Files; import javafx.event.EventHandler; +import javafx.geometry.Pos; import javafx.scene.Cursor; -import javafx.scene.control.Alert; +import javafx.scene.control.*; import javafx.scene.control.Alert.AlertType; -import javafx.scene.control.TextField; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.input.InputEvent; @@ -35,7 +35,7 @@ public class UserSettingsPane extends SettingsPane { private boolean profilePicChanged, usernameChanged, passwordChanged, validPassword; private byte[] currentImageBytes; - private String newUsername, newPassword; + private String newUsername, newPassword = ""; /** * Creates a new {@code UserSettingsPane}. @@ -81,7 +81,8 @@ public class UserSettingsPane extends SettingsPane { hbox.getChildren().add(profilePic); // Displaying the username change mechanism - final var username = user.getName(); + final var username = user.getName(); + newUsername = username; final var usernameTextField = new TextField(username); final EventHandler textChanged = e -> { newUsername = usernameTextField.getText(); @@ -91,6 +92,39 @@ public class UserSettingsPane extends SettingsPane { usernameTextField.setOnKeyTyped(textChanged); hbox.getChildren().add(usernameTextField); vbox.getChildren().add(hbox); + + // "Displaying" the password change mechanism + final HBox[] passwordHBoxes = { new HBox(), new HBox(), new HBox() }; + final Label[] passwordLabels = { new Label("Enter current password:"), new Label("Enter new password:"), + new Label("Repeat new password:") }; + + final var currentPasswordField = new PasswordField(); + final var newPasswordField = new PasswordField(); + final var repeatNewPasswordField = new PasswordField(); + final PasswordField[] passwordFields = { currentPasswordField, newPasswordField, repeatNewPasswordField }; + final EventHandler passwordEntered = e -> { + newPassword = newPasswordField.getText(); + validPassword = newPassword.equals(repeatNewPasswordField.getText()) + && !newPasswordField.getText().isBlank(); + }; + newPasswordField.setOnInputMethodTextChanged(passwordEntered); + newPasswordField.setOnKeyTyped(passwordEntered); + repeatNewPasswordField.setOnInputMethodTextChanged(passwordEntered); + repeatNewPasswordField.setOnKeyTyped(passwordEntered); + + for (int i = 0; i < passwordHBoxes.length; i++) { + final var hBox2 = passwordHBoxes[i]; + hBox2.getChildren().add(passwordLabels[i]); + hBox2.getChildren().add(passwordFields[i]); + vbox.getChildren().add(hBox2); + } + + // Displaying the save button + final var saveButton = new Button("Save"); + saveButton.setOnAction(e -> save(user.getID(), currentPasswordField.getText())); + saveButton.setAlignment(Pos.BOTTOM_RIGHT); + vbox.getChildren().add(saveButton); + } /** @@ -118,7 +152,7 @@ public class UserSettingsPane extends SettingsPane { // The password was changed if (passwordChanged && validPassword) eventbus.dispatch(new SendEvent(new PasswordChangeRequest(newPassword, oldPassword, userID))); - else if (!(validPassword || newPassword != null && newPassword.isBlank())) { + else if (!(validPassword || newPassword.isBlank())) { final var alert = new Alert(AlertType.ERROR); alert.setTitle("Unequal Password"); alert.setContentText("Repeated password is unequal to the chosen new password"); diff --git a/common/src/main/java/envoy/event/PasswordChangeRequest.java b/common/src/main/java/envoy/event/PasswordChangeRequest.java new file mode 100644 index 0000000..9b16a5d --- /dev/null +++ b/common/src/main/java/envoy/event/PasswordChangeRequest.java @@ -0,0 +1,44 @@ +package envoy.event; + +import envoy.data.Contact; + +/** + * Project: envoy-common
+ * File: PasswordChangeRequest.java
+ * Created: 31.07.2020
+ * + * @author Leon Hofmeister + * @since Envoy Common v0.2-beta + */ +public class PasswordChangeRequest extends Event { + + private final long id; + + private final String oldPassword; + + private static final long serialVersionUID = 0L; + + /** + * @param newPassword the new password of that user + * @param oldPassword the old password of that user + * @param userID the ID of the user who wants to change his password + * @since Envoy Common v0.2-beta + */ + public PasswordChangeRequest(String newPassword, String oldPassword, long userID) { + super(newPassword); + this.oldPassword = oldPassword; + id = userID; + } + + /** + * @return the ID of the {@link Contact} this event is related to + * @since Envoy Common v0.2-alpha + */ + public long getID() { return id; } + + /** + * @return the old password of the underlying user + * @since Envoy Common v0.2-beta + */ + public String getOldPassword() { return oldPassword; } +} diff --git a/common/src/main/java/envoy/event/PasswordChangeResult.java b/common/src/main/java/envoy/event/PasswordChangeResult.java new file mode 100644 index 0000000..6252980 --- /dev/null +++ b/common/src/main/java/envoy/event/PasswordChangeResult.java @@ -0,0 +1,26 @@ +package envoy.event; + +/** + * This class acts as a notice to the user whether his + * {@link envoy.event.PasswordChangeRequest} was successful. + *

+ * Project: envoy-common
+ * File: PasswordChangeResult.java
+ * Created: 01.08.2020
+ * + * @author Leon Hofmeister + * @since Envoy Common v0.2-beta + */ +public class PasswordChangeResult extends Event { + + private static final long serialVersionUID = 1L; + + /** + * Creates an instance of {@code PasswordChangeResult}. + * + * @param value whether the preceding {@link envoy.event.PasswordChangeRequest} + * was successful. + * @since Envoy Common v0.2-beta + */ + public PasswordChangeResult(boolean value) { super(value); } +} diff --git a/server/src/main/java/envoy/server/processors/PasswordChangeRequestProcessor.java b/server/src/main/java/envoy/server/processors/PasswordChangeRequestProcessor.java new file mode 100644 index 0000000..7d41c7f --- /dev/null +++ b/server/src/main/java/envoy/server/processors/PasswordChangeRequestProcessor.java @@ -0,0 +1,29 @@ +package envoy.server.processors; + +import java.io.IOException; + +import envoy.event.PasswordChangeRequest; +import envoy.event.PasswordChangeResult; +import envoy.server.data.PersistenceManager; +import envoy.server.net.ObjectWriteProxy; +import envoy.server.util.PasswordUtil; + +/** + * Project: envoy-server-standalone
+ * File: PasswordChangeRequestProcessor.java
+ * Created: 31.07.2020
+ * + * @author Leon Hofmeister + * @since Envoy Server v0.2-beta + */ +public class PasswordChangeRequestProcessor implements ObjectProcessor { + + @Override + public void process(PasswordChangeRequest event, long socketID, ObjectWriteProxy writeProxy) throws IOException { + final var persistenceManager = PersistenceManager.getInstance(); + final var user = persistenceManager.getUserByID(event.getID()); + final var correctAuthentication = PasswordUtil.validate(event.getOldPassword(), user.getPasswordHash()); + if (correctAuthentication) user.setPasswordHash(PasswordUtil.hash(event.get())); + writeProxy.write(socketID, new PasswordChangeResult(correctAuthentication)); + } +} From fab573efe19aefb7b97621c89e527c9e872e7f00 Mon Sep 17 00:00:00 2001 From: delvh Date: Sat, 1 Aug 2020 14:57:08 +0200 Subject: [PATCH 5/8] Added logging and fixed some security concerns --- .../client/ui/settings/UserSettingsPane.java | 36 ++++++++++++++----- .../envoy/event/PasswordChangeRequest.java | 3 ++ .../src/main/java/envoy/server/Startup.java | 4 ++- .../main/java/envoy/server/data/Contact.java | 2 +- .../PasswordChangeRequestProcessor.java | 8 ++++- 5 files changed, 41 insertions(+), 12 deletions(-) diff --git a/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java b/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java index 1b5e2de..cb33fd4 100644 --- a/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java +++ b/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java @@ -4,6 +4,8 @@ import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.util.Arrays; +import java.util.logging.Level; import javafx.event.EventHandler; import javafx.geometry.Pos; @@ -22,6 +24,7 @@ import envoy.client.ui.SceneContext; import envoy.data.User; import envoy.event.*; import envoy.util.Bounds; +import envoy.util.EnvoyLog; /** * Project: envoy-client
@@ -33,8 +36,8 @@ import envoy.util.Bounds; */ public class UserSettingsPane extends SettingsPane { - private boolean profilePicChanged, usernameChanged, passwordChanged, validPassword; - private byte[] currentImageBytes; + private boolean profilePicChanged, usernameChanged, validPassword; + private byte[] currentImageBytes, originalImageBytes; private String newUsername, newPassword = ""; /** @@ -52,12 +55,14 @@ public class UserSettingsPane extends SettingsPane { // TODO: display current profile pic final var profilePic = new ImageView(IconUtil.loadIcon("envoy_logo", 50)); profilePic.setCursor(Cursor.HAND); + profilePic.setFitWidth(50); + profilePic.setFitHeight(50); profilePic.setOnMouseClicked(e -> { final var pictureChooser = new FileChooser(); pictureChooser.setTitle("Select a new picture"); pictureChooser.setInitialDirectory(new File(System.getProperty("user.home"))); - pictureChooser.setSelectedExtensionFilter(new FileChooser.ExtensionFilter("Pictures", "*.png", "*.jpg", "*.bmp", "*.gif")); + pictureChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Pictures", "*.png", "*.jpg", "*.bmp", "*.gif")); final var file = pictureChooser.showOpenDialog(sceneContext.getStage()); @@ -134,25 +139,38 @@ public class UserSettingsPane extends SettingsPane { * @since Envoy Client v0.2-beta */ private void save(long userID, String oldPassword) { - final var eventbus = EventBus.getInstance(); + final var eventBus = EventBus.getInstance(); + final var logger = EnvoyLog.getLogger(UserSettingsPane.class); // The profile pic was changed - if (profilePicChanged) eventbus.dispatch(new SendEvent(new ProfilePicChange(currentImageBytes, userID))); + if (profilePicChanged && !Arrays.equals(currentImageBytes, originalImageBytes)) { + final var profilePicChangeEvent = new ProfilePicChange(currentImageBytes, userID); + eventBus.dispatch(profilePicChangeEvent); + eventBus.dispatch(new SendEvent(profilePicChangeEvent)); + logger.log(Level.INFO, "The user just changed his profile pic."); + } // The username was changed final var validContactName = Bounds.isValidContactName(newUsername); - if (usernameChanged && validContactName) eventbus.dispatch(new SendEvent(new NameChange(userID, newUsername))); - else if (!validContactName) { + if (usernameChanged && validContactName) { + final var nameChangeEvent = new NameChange(userID, newUsername); + eventBus.dispatch(new SendEvent(nameChangeEvent)); + eventBus.dispatch(nameChangeEvent); + logger.log(Level.INFO, "The user just changed his name to " + newUsername + "."); + } else if (!validContactName) { final var alert = new Alert(AlertType.ERROR); alert.setTitle("Invalid username"); alert.setContentText("The entered username does not conform with the naming limitations: " + Bounds.CONTACT_NAME_PATTERN); alert.showAndWait(); + logger.log(Level.INFO, "An invalid username was requested."); return; } // The password was changed - if (passwordChanged && validPassword) eventbus.dispatch(new SendEvent(new PasswordChangeRequest(newPassword, oldPassword, userID))); - else if (!(validPassword || newPassword.isBlank())) { + if (validPassword) { + eventBus.dispatch(new SendEvent(new PasswordChangeRequest(newPassword, oldPassword, userID))); + logger.log(Level.INFO, "The user just tried to change his password!"); + } else if (!(validPassword || newPassword.isBlank())) { final var alert = new Alert(AlertType.ERROR); alert.setTitle("Unequal Password"); alert.setContentText("Repeated password is unequal to the chosen new password"); diff --git a/common/src/main/java/envoy/event/PasswordChangeRequest.java b/common/src/main/java/envoy/event/PasswordChangeRequest.java index 9b16a5d..3029df8 100644 --- a/common/src/main/java/envoy/event/PasswordChangeRequest.java +++ b/common/src/main/java/envoy/event/PasswordChangeRequest.java @@ -41,4 +41,7 @@ public class PasswordChangeRequest extends Event { * @since Envoy Common v0.2-beta */ public String getOldPassword() { return oldPassword; } + + @Override + public String toString() { return "PasswordChangeRequest[id=" + id + "]"; } } diff --git a/server/src/main/java/envoy/server/Startup.java b/server/src/main/java/envoy/server/Startup.java index bd00a66..c131fce 100755 --- a/server/src/main/java/envoy/server/Startup.java +++ b/server/src/main/java/envoy/server/Startup.java @@ -71,7 +71,9 @@ public class Startup { new UserSearchProcessor(), new ContactOperationProcessor(), new IsTypingProcessor(), - new NameChangeProcessor()))); + new NameChangeProcessor(), + new ProfilePicChangeProcessor(), + new PasswordChangeRequestProcessor()))); // Initialize the current message ID final PersistenceManager persistenceManager = PersistenceManager.getInstance(); diff --git a/server/src/main/java/envoy/server/data/Contact.java b/server/src/main/java/envoy/server/data/Contact.java index 43a9983..a1e626d 100644 --- a/server/src/main/java/envoy/server/data/Contact.java +++ b/server/src/main/java/envoy/server/data/Contact.java @@ -18,7 +18,7 @@ import javax.persistence.*; */ @Entity -@Table(name = "contacts") +@Table(name = "contacts", uniqueConstraints = { @UniqueConstraint(columnNames = { "name" }) }) @Inheritance(strategy = InheritanceType.SINGLE_TABLE) public abstract class Contact { diff --git a/server/src/main/java/envoy/server/processors/PasswordChangeRequestProcessor.java b/server/src/main/java/envoy/server/processors/PasswordChangeRequestProcessor.java index 7d41c7f..84ebc7a 100644 --- a/server/src/main/java/envoy/server/processors/PasswordChangeRequestProcessor.java +++ b/server/src/main/java/envoy/server/processors/PasswordChangeRequestProcessor.java @@ -1,12 +1,14 @@ package envoy.server.processors; import java.io.IOException; +import java.util.logging.Level; import envoy.event.PasswordChangeRequest; import envoy.event.PasswordChangeResult; import envoy.server.data.PersistenceManager; import envoy.server.net.ObjectWriteProxy; import envoy.server.util.PasswordUtil; +import envoy.util.EnvoyLog; /** * Project: envoy-server-standalone
@@ -22,8 +24,12 @@ public class PasswordChangeRequestProcessor implements ObjectProcessor Date: Sat, 1 Aug 2020 21:40:20 +0200 Subject: [PATCH 6/8] Made system commands case insensitive and reworked /dabr mechanism --- .../data/commands/SystemCommandsMap.java | 6 ++++-- .../envoy/client/ui/controller/ChatScene.java | 20 ++++++++++++++++--- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/client/src/main/java/envoy/client/data/commands/SystemCommandsMap.java b/client/src/main/java/envoy/client/data/commands/SystemCommandsMap.java index cfcb0a4..ed75f24 100644 --- a/client/src/main/java/envoy/client/data/commands/SystemCommandsMap.java +++ b/client/src/main/java/envoy/client/data/commands/SystemCommandsMap.java @@ -37,7 +37,9 @@ public final class SystemCommandsMap { * @see SystemCommandsMap#isValidKey(String) * @since Envoy Client v0.2-beta */ - public void add(String command, SystemCommand systemCommand) { if (isValidKey(command)) systemCommands.put(command, systemCommand); } + public void add(String command, SystemCommand systemCommand) { + if (isValidKey(command)) systemCommands.put(command.toLowerCase(), systemCommand); + } /** * This method checks if the input String is a key in the map and returns the @@ -60,7 +62,7 @@ public final class SystemCommandsMap { * @return the wrapped system command, if present * @since Envoy Client v0.2-beta */ - public Optional get(String input) { return Optional.ofNullable(systemCommands.get(getCommand(input))); } + public Optional get(String input) { return Optional.ofNullable(systemCommands.get(getCommand(input.toLowerCase()))); } /** * This method ensures that the "/" of a {@link SystemCommand} is stripped.
diff --git a/client/src/main/java/envoy/client/ui/controller/ChatScene.java b/client/src/main/java/envoy/client/ui/controller/ChatScene.java index 9af7c62..ba8346a 100644 --- a/client/src/main/java/envoy/client/ui/controller/ChatScene.java +++ b/client/src/main/java/envoy/client/ui/controller/ChatScene.java @@ -10,9 +10,11 @@ import java.io.IOException; import java.nio.file.Files; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.util.Arrays; import java.util.Random; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; import javafx.animation.RotateTransition; import javafx.application.Platform; @@ -423,9 +425,21 @@ public final class ChatScene implements Restorable { * @since Envoy Client v0.1-beta */ private void doABarrelRoll(int rotations, double animationTime) { - // contains all Node objects in ChatScene in alphabetical order - final var rotatableNodes = new Node[] { attachmentButton, attachmentView, contactLabel, infoLabel, messageList, messageTextArea, postButton, - remainingChars, rotateButton, scene, settingsButton, chatList, voiceButton }; + // Limiting the rotations and duration + rotations = Math.min(rotations, 100000); + rotations = Math.max(rotations, 1); + animationTime = Math.min(animationTime, 150); + animationTime = Math.max(animationTime, 0.25); + + // contains all Node objects in ChatScene + final var rotatableNodes = Arrays.stream(ChatScene.class.getDeclaredFields()).map(field -> { + try { + return field.get(this); + } catch (IllegalArgumentException | IllegalAccessException e1) { + // In this case, this option can never be executed + return null; + } + }).filter(Node.class::isInstance).map(Node.class::cast).collect(Collectors.toList()); for (final var node : rotatableNodes) { // Sets the animation duration to {animationTime} final var rotateTransition = new RotateTransition(Duration.seconds(animationTime), node); From 615c317598dbe1895594d8f68e8bd67d149c14c6 Mon Sep 17 00:00:00 2001 From: delvh Date: Mon, 3 Aug 2020 15:10:35 +0200 Subject: [PATCH 7/8] Apply code review suggestions from @CyB3RC0nN0R 2 --- .../java/envoy/client/ui/listcell/AbstractListCell.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/src/main/java/envoy/client/ui/listcell/AbstractListCell.java b/client/src/main/java/envoy/client/ui/listcell/AbstractListCell.java index 0bbe20b..2a527fd 100644 --- a/client/src/main/java/envoy/client/ui/listcell/AbstractListCell.java +++ b/client/src/main/java/envoy/client/ui/listcell/AbstractListCell.java @@ -35,8 +35,10 @@ public abstract class AbstractListCell extends ListCell { @Override protected final void updateItem(T item, boolean empty) { super.updateItem(item, empty); - setGraphic(empty || item == null ? null : renderItem(item)); - if (!empty) setCursor(Cursor.HAND); + if (!(empty || item == null)) { + setCursor(Cursor.HAND); + setGraphic(renderItem(item)); + } } /** From efaa3eb8c3598dc113155a978c9c4e08930d47f5 Mon Sep 17 00:00:00 2001 From: CyB3RC0nN0R Date: Mon, 3 Aug 2020 22:07:12 +0200 Subject: [PATCH 8/8] Fix edge case in AbstractListCell Clear the cell if the item is updated with a null value. --- .../main/java/envoy/client/ui/listcell/AbstractListCell.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/src/main/java/envoy/client/ui/listcell/AbstractListCell.java b/client/src/main/java/envoy/client/ui/listcell/AbstractListCell.java index 2a527fd..45e424d 100644 --- a/client/src/main/java/envoy/client/ui/listcell/AbstractListCell.java +++ b/client/src/main/java/envoy/client/ui/listcell/AbstractListCell.java @@ -38,6 +38,8 @@ public abstract class AbstractListCell extends ListCell { if (!(empty || item == null)) { setCursor(Cursor.HAND); setGraphic(renderItem(item)); + } else { + setGraphic(null); } }