Add Local Account Deletion #108

Merged
kske merged 3 commits from f/account-deletion into develop 2020-10-31 16:57:10 +01:00
15 changed files with 75 additions and 100 deletions
Showing only changes of commit e5659c1da1 - Show all commits

View File

@ -283,6 +283,15 @@ public final class LocalDB implements EventListener {
* @since Envoy Client v0.3-beta * @since Envoy Client v0.3-beta
*/ */
public void delete() { public void delete() {
try {
// Save ID generator - can be used for other users in that db
if (hasIDGenerator())
SerializationUtils.write(idGeneratorFile, idGenerator);
} catch (final IOException e) {
EnvoyLog.getLogger(LocalDB.class).log(Level.SEVERE, "Unable to save local database: ",
e);
}
if (lastLoginFile != null) if (lastLoginFile != null)
lastLoginFile.delete(); lastLoginFile.delete();
userFile.delete(); userFile.delete();
@ -427,10 +436,7 @@ public final class LocalDB implements EventListener {
if (user.getID() == deletion.get()) if (user.getID() == deletion.get())
logger.log(Level.WARNING, logger.log(Level.WARNING,
"I have been informed by the server that I have been deleted without even knowing it..."); "I have been informed by the server that I have been deleted without even knowing it...");
getChat(deletion.get()).ifPresent(chat -> { getChat(deletion.get()).ifPresent(chat -> chat.setDisabled(true));
chat.setDisabled(true);
chat.setUnderlyingContactDeleted(true);
});
} }
/** /**

View File

@ -1,4 +1,4 @@
package envoy.event.contact; package envoy.client.event;
import envoy.event.Event; import envoy.event.Event;

View File

@ -18,7 +18,7 @@ import envoy.event.contact.*;
import envoy.util.Bounds; import envoy.util.Bounds;
import envoy.client.data.*; import envoy.client.data.*;
import envoy.client.event.BackEvent; import envoy.client.event.*;
import envoy.client.ui.control.*; import envoy.client.ui.control.*;
import envoy.client.ui.listcell.ListCellFactory; import envoy.client.ui.listcell.ListCellFactory;

View File

@ -13,6 +13,7 @@ import javafx.scene.image.*;
import javafx.scene.input.InputEvent; import javafx.scene.input.InputEvent;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import javafx.stage.FileChooser; import javafx.stage.FileChooser;
import javafx.util.Duration;
import dev.kske.eventbus.EventBus; import dev.kske.eventbus.EventBus;
@ -38,7 +39,7 @@ public final class UserSettingsPane extends OnlineOnlySettingsPane {
private final PasswordField newPasswordField = new PasswordField(); private final PasswordField newPasswordField = new PasswordField();
private final PasswordField repeatNewPasswordField = new PasswordField(); private final PasswordField repeatNewPasswordField = new PasswordField();
private final Button saveButton = new Button("Save"); private final Button saveButton = new Button("Save");
private final Button deleteAccountButton = new Button("Delete Account"); private final Button deleteAccountButton = new Button("Delete Account (Locally)");
private static final EventBus eventBus = EventBus.getInstance(); private static final EventBus eventBus = EventBus.getInstance();
private static final Logger logger = EnvoyLog.getLogger(UserSettingsPane.class); private static final Logger logger = EnvoyLog.getLogger(UserSettingsPane.class);
@ -148,6 +149,13 @@ public final class UserSettingsPane extends OnlineOnlySettingsPane {
// Displaying the delete account button // Displaying the delete account button
deleteAccountButton.setAlignment(Pos.BASELINE_CENTER); deleteAccountButton.setAlignment(Pos.BASELINE_CENTER);
deleteAccountButton.setOnAction(e -> UserUtil.deleteAccount()); deleteAccountButton.setOnAction(e -> UserUtil.deleteAccount());
deleteAccountButton.setText("Delete Account (locally)");
deleteAccountButton.setPrefHeight(25);
deleteAccountButton.getStyleClass().clear();
deleteAccountButton.getStyleClass().add("danger-button");
final var tooltip = new Tooltip("Remote deletion is currently unsupported.");
tooltip.setShowDelay(Duration.millis(100));
deleteAccountButton.setTooltip(tooltip);
getChildren().add(deleteAccountButton); getChildren().add(deleteAccountButton);
} }

View File

@ -10,7 +10,7 @@ import dev.kske.eventbus.EventBus;
import envoy.data.*; import envoy.data.*;
import envoy.data.User.UserStatus; import envoy.data.User.UserStatus;
import envoy.event.*; import envoy.event.*;
import envoy.event.contact.*; import envoy.event.contact.UserOperation;
import envoy.util.EnvoyLog; import envoy.util.EnvoyLog;
import envoy.client.data.Context; import envoy.client.data.Context;
@ -129,32 +129,27 @@ public final class UserUtil {
* @since Envoy Client v0.3-beta * @since Envoy Client v0.3-beta
*/ */
public static void deleteAccount() { public static void deleteAccount() {
if (!context.getClient().isOnline())
return;
else {
// Show the first wall of defense, if not disabled by the user // Show the first wall of defense, if not disabled by the user
final var outerAlert = new Alert(AlertType.CONFIRMATION); final var outerAlert = new Alert(AlertType.CONFIRMATION);
outerAlert.setContentText( outerAlert.setContentText(
"Are you sure you want to delete your account entirely? This action can seriously not be undone."); "Are you sure you want to delete your account entirely? This action can seriously not be undone.");
outerAlert.setTitle("Delete Account?"); outerAlert.setTitle("Delete Account?");
AlertHelper.confirmAction(outerAlert, () -> { AlertHelper.confirmAction(outerAlert, () -> {
// Show the final wall of defense in every case // Show the final wall of defense in every case
final var lastAlert = new Alert(AlertType.WARNING, final var lastAlert = new Alert(AlertType.WARNING,
"Do you REALLY want to delete your account? Last Warning. Proceed?", "Do you REALLY want to delete your account? Last Warning. Proceed?",
ButtonType.CANCEL, ButtonType.OK); ButtonType.CANCEL, ButtonType.OK);
lastAlert.setTitle("Delete Account?"); lastAlert.setTitle("Delete Account?");
lastAlert.showAndWait().filter(ButtonType.OK::equals).ifPresent(b2 -> { lastAlert.showAndWait().filter(ButtonType.OK::equals).ifPresent(b2 -> {
// Delete the account // Delete the account
context.getClient() // TODO: Notify server of account deletion
.send(new AccountDeletion(context.getLocalDB().getUser().getID())); context.getLocalDB().delete();
context.getLocalDB().delete(); logger.log(Level.INFO, "The user just deleted his account. Goodbye.");
logger.log(Level.INFO, "The user just deleted his account. Goodbye."); ShutdownHelper.exit(true);
ShutdownHelper.exit(true);
});
}); });
} });
} }
} }

View File

@ -70,6 +70,17 @@
-fx-text-fill: gray; -fx-text-fill: gray;
} }
.danger-button {
-fx-background-color: red;
-fx-text-fill: white;
-fx-background-radius: 0.2em;
}
.danger-button:hover {
-fx-scale-x: 1.05;
-fx-scale-y: 1.05;
}
.received-message { .received-message {
-fx-alignment: center-left; -fx-alignment: center-left;
-fx-background-radius: 1.3em; -fx-background-radius: 1.3em;

View File

@ -30,6 +30,10 @@
-fx-background-color: black; -fx-background-color: black;
} }
.tooltip {
-fx-text-fill: black;
}
#login-input-field { #login-input-field {
-fx-border-color: black; -fx-border-color: black;
} }

View File

@ -59,8 +59,7 @@ public final class Startup {
new NameChangeProcessor(), new NameChangeProcessor(),
new ProfilePicChangeProcessor(), new ProfilePicChangeProcessor(),
new PasswordChangeRequestProcessor(), new PasswordChangeRequestProcessor(),
new IssueProposalProcessor(), new IssueProposalProcessor())));
new AccountDeletionProcessor())));
// Initialize the current message ID // Initialize the current message ID
final var persistenceManager = PersistenceManager.getInstance(); final var persistenceManager = PersistenceManager.getInstance();

View File

@ -27,18 +27,14 @@ import envoy.data.Message.MessageStatus;
@Entity @Entity
@Table(name = "messages") @Table(name = "messages")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@NamedQueries({ @NamedQuery(name = Message.getPending, query = "SELECT m FROM Message m WHERE "
@NamedQuery(name = Message.getPending, query = "SELECT m FROM Message m WHERE " // Send to or by the user before last seen
// Send to or by the user before last seen + "(m.sender = :user OR m.recipient = :user) AND m.creationDate > :lastSeen "
+ "(m.sender = :user OR m.recipient = :user) AND m.creationDate > :lastSeen " // SENT to the user
// SENT to the user + "OR m.recipient = :user AND m.status = envoy.data.Message$MessageStatus.SENT "
+ "OR m.recipient = :user AND m.status = envoy.data.Message$MessageStatus.SENT " // Sent by the user and RECEIVED / READ after last seen
// Sent by the user and RECEIVED / READ after last seen + "OR m.sender = :user AND (m.status = envoy.data.Message$MessageStatus.RECEIVED AND m.receivedDate > :lastSeen "
+ "OR m.sender = :user AND (m.status = envoy.data.Message$MessageStatus.RECEIVED AND m.receivedDate > :lastSeen " + "OR m.status = envoy.data.Message$MessageStatus.READ AND m.readDate > :lastSeen)")
+ "OR m.status = envoy.data.Message$MessageStatus.READ AND m.readDate > :lastSeen)"),
@NamedQuery(name = Message.deleteByRecipient, query = "DELETE FROM Message m WHERE m.recipient = :deleted OR m.sender = :deleted")
})
public class Message { public class Message {
/** /**
@ -49,13 +45,6 @@ public class Message {
*/ */
public static final String getPending = "Message.getPending"; public static final String getPending = "Message.getPending";
/**
* Named query deleting all messages of a user (parameter {@code :deleted}).
*
* @since Envoy Server v0.3-beta
*/
public static final String deleteByRecipient = "Message.deleteByRecipient";
@Id @Id
protected long id; protected long id;

View File

@ -121,12 +121,9 @@ public final class PersistenceManager {
transaction(() -> { transaction(() -> {
// Remove this contact from the contact list of his contacts // Remove this contact from the contact list of his contacts
for (final var remainingContact : contact.getContacts()) for (final var remainingContact : contact.contacts)
remainingContact.getContacts().remove(contact); remainingContact.getContacts().remove(contact);
contact.contacts.clear();
entityManager
.createNamedQuery(Message.deleteByRecipient).setParameter("deleted", contact)
.executeUpdate();
}); });
remove(contact); remove(contact);
} }

View File

@ -1,33 +0,0 @@
package envoy.server.processors;
import java.io.IOException;
import java.time.Instant;
import envoy.event.contact.AccountDeletion;
import envoy.server.data.*;
import envoy.server.net.ObjectWriteProxy;
/**
* @author Leon Hofmeister
* @since Envoy Server v0.3-beta
*/
public class AccountDeletionProcessor implements ObjectProcessor<AccountDeletion> {
private static final PersistenceManager persistenceManager = PersistenceManager.getInstance();
@Override
public void process(AccountDeletion input, long socketID, ObjectWriteProxy writeProxy)
throws IOException {
final var contact = persistenceManager.getContactByID(input.get());
contact.getContacts().forEach(c -> {
persistenceManager.removeContactBidirectional(contact, c);
if (c instanceof User)
((User) c).setLatestContactDeletion(Instant.now());
});
writeProxy.writeToOnlineContacts(contact.getContacts(), input);
persistenceManager.deleteContact(contact);
}
}

View File

@ -38,6 +38,8 @@ public final class GroupMessageStatusChangeProcessor
} }
GroupMessage gmsg = (GroupMessage) persistenceManager.getMessageByID(statusChange.getID()); GroupMessage gmsg = (GroupMessage) persistenceManager.getMessageByID(statusChange.getID());
if (gmsg == null)
return;
// Any other status than READ is not supposed to be sent to the server // Any other status than READ is not supposed to be sent to the server
if (statusChange.get() != MessageStatus.READ) { if (statusChange.get() != MessageStatus.READ) {

View File

@ -4,7 +4,6 @@ import java.time.Instant;
import java.util.logging.Level; import java.util.logging.Level;
import envoy.event.GroupResize; import envoy.event.GroupResize;
import envoy.event.contact.AccountDeletion;
import envoy.util.EnvoyLog; import envoy.util.EnvoyLog;
import envoy.server.data.*; import envoy.server.data.*;
@ -25,11 +24,9 @@ public final class GroupResizeProcessor implements ObjectProcessor<GroupResize>
final var group = persistenceManager.getGroupByID(groupResize.getGroupID()); final var group = persistenceManager.getGroupByID(groupResize.getGroupID());
final var sender = persistenceManager.getUserByID(groupResize.get().getID()); final var sender = persistenceManager.getUserByID(groupResize.get().getID());
// Inform the sender that this group has already been deleted // TODO: Inform the sender that this group has already been deleted
if (group == null) { if (group == null)
writeProxy.write(socketID, new AccountDeletion(groupResize.getGroupID()));
return; return;
}
// Perform the desired operation // Perform the desired operation
switch (groupResize.getOperation()) { switch (groupResize.getOperation()) {

View File

@ -32,6 +32,8 @@ public final class MessageStatusChangeProcessor implements ObjectProcessor<Messa
} }
final var msg = persistenceManager.getMessageByID(statusChange.getID()); final var msg = persistenceManager.getMessageByID(statusChange.getID());
if (msg == null)
return;
msg.read(); msg.read();
persistenceManager.updateMessage(msg); persistenceManager.updateMessage(msg);

View File

@ -4,7 +4,7 @@ import java.time.Instant;
import java.util.logging.*; import java.util.logging.*;
import envoy.event.ElementOperation; import envoy.event.ElementOperation;
import envoy.event.contact.*; import envoy.event.contact.UserOperation;
import envoy.util.EnvoyLog; import envoy.util.EnvoyLog;
import envoy.server.data.PersistenceManager; import envoy.server.data.PersistenceManager;
@ -27,11 +27,9 @@ public final class UserOperationProcessor implements ObjectProcessor<UserOperati
final long contactID = evt.get().getID(); final long contactID = evt.get().getID();
final var recipient = persistenceManager.getUserByID(contactID); final var recipient = persistenceManager.getUserByID(contactID);
// Inform the sender if the requested contact has already been deleted // TODO: Inform the sender if the requested contact has already been deleted
if (recipient == null) { if (recipient == null)
writeProxy.write(socketID, new AccountDeletion(contactID));
return; return;
}
final var sender = persistenceManager.getUserByID(userID); final var sender = persistenceManager.getUserByID(userID);
switch (evt.getOperationType()) { switch (evt.getOperationType()) {