From 117904c7133fd9bb645a8fb671349e3460697fe7 Mon Sep 17 00:00:00 2001 From: Marcelo Schmidt Date: Wed, 2 Mar 2016 16:42:43 -0300 Subject: [PATCH 1/4] Adds ability for users to delete their own accounts --- i18n/en.i18n.json | 6 ++++ packages/rocketchat-lib/package.js | 1 + .../server/methods/deleteUserOwnAccount.js | 35 +++++++++++++++++++ .../assets/stylesheets/base.less | 4 +++ .../account/accountPreferences.coffee | 25 +++++++++++++ .../account/accountPreferences.html | 3 ++ packages/rocketchat-ui-account/package.js | 3 +- 7 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 packages/rocketchat-lib/server/methods/deleteUserOwnAccount.js diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json index 21563e91294..2813704d6a0 100644 --- a/i18n/en.i18n.json +++ b/i18n/en.i18n.json @@ -92,6 +92,7 @@ "are_also_typing" : "are also typing", "are_typing" : "are typing", "Are_you_sure" : "Are you sure?", + "Are_you_sure_you_want_to_delete_your_account" : "Are you sure you want to delete your account?", "Authorization_URL" : "Authorization URL", "Authorize" : "Authorize", "Auto_Load_Images" : "Auto Load Images", @@ -146,6 +147,7 @@ "days" : "days", "Deactivate" : "Deactivate", "Default" : "Default", + "Delete_my_account" : "Delete my account", "Delete_Room_Warning" : "Deleting a room will delete all messages posted within the room. This cannot be undone.", "Delete_User_Warning" : "Deleting a user will delete all messages from that user as well. This cannot be undone.", "Deleted" : "Deleted!", @@ -214,6 +216,7 @@ "Highlights_List" : "Highlight words", "History" : "History", "hours" : "hours", + "If_you_are_sure_type_in_your_password" : "If you are sure type in your password:", "Incorrect_Password" : "Incorrect Password", "inline_code" : "inline_code", "Install_Extension" : "Install Extension", @@ -434,6 +437,7 @@ "Please_wait" : "Please wait", "Please_wait_activation" : "Please wait, this can take some time.", "Please_wait_statistics" : "Please wait, statistics are being generated.", + "Please_wait_while_your_account_is_being_deleted" : "Please wait while your account is being deleted...", "Post_as" : "Post as", "Post_to_Channel" : "Post to Channel", "Post_to_s_as_s" : "Post to %s as %s", @@ -686,6 +690,7 @@ "You_need_confirm_email" : "You need to confirm your email to login!", "You_need_install_an_extension_to_allow_screen_sharing" : "You need install an extension to allow screen sharing", "You_need_to_change_your_password" : "You need to change your password", + "You_need_to_type_in_your_password_in_order_to_do_this" : "You need to type in your password in order to do this!", "You_should_name_it_to_easily_manage_your_integrations" : "You should name it to easily manage your integrations.", "You_will_not_be_able_to_recover" : "You will not be able to recover this message!", "You_will_not_be_able_to_recover_file" : "You will not be able to recover this file!", @@ -693,5 +698,6 @@ "Your_file_has_been_deleted" : "Your file has been deleted.", "Your_mail_was_sent_to_s" : "Your mail was sent to %s", "Your_Open_Source_solution" : "Your own Open Source chat solution", + "Your_password_is_wrong" : "Your password is wrong!", "Your_push_was_sent_to_s_devices" : "Your push was sent to %s devices" } \ No newline at end of file diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index 2fb6c0c807a..59536a0a313 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -75,6 +75,7 @@ Package.onUse(function(api) { api.addFiles('server/methods/addOAuthService.coffee', 'server'); api.addFiles('server/methods/checkRegistrationSecretURL.coffee', 'server'); api.addFiles('server/methods/clearRequirePasswordChange.js', 'server'); + api.addFiles('server/methods/deleteUserOwnAccount.js', 'server'); api.addFiles('server/methods/joinDefaultChannels.coffee', 'server'); api.addFiles('server/methods/removeOAuthService.coffee', 'server'); api.addFiles('server/methods/robotMethods.coffee', 'server'); diff --git a/packages/rocketchat-lib/server/methods/deleteUserOwnAccount.js b/packages/rocketchat-lib/server/methods/deleteUserOwnAccount.js new file mode 100644 index 00000000000..66726978f9f --- /dev/null +++ b/packages/rocketchat-lib/server/methods/deleteUserOwnAccount.js @@ -0,0 +1,35 @@ +Meteor.methods({ + deleteUserOwnAccount: function(password) { + if (!Meteor.userId()) { + throw new Meteor.Error('invalid-user', "[methods] deleteUserOwnAccount -> Invalid user"); + } + + const user = RocketChat.models.Users.findOneById(Meteor.userId()); + + result = Accounts._checkPassword(user, { digest: password, algorithm: 'sha-256' }); + if (result.error) { + throw new Meteor.Error('invalid-password', "[methods] deleteUserOwnAccount -> Invalid password"); + } + + RocketChat.models.Messages.removeByUserId(Meteor.userId()); // Remove user messages + RocketChat.models.Subscriptions.findByUserId(Meteor.userId()).forEach((subscription) => { + let room = RocketChat.models.Rooms.findOneById(subscription.rid); + if (room) { + if (room.t !== 'c' && room.usernames.length === 1) { + RocketChat.models.Rooms.removeById(subscription.rid); // Remove non-channel rooms with only 1 user (the one being deleted) + } + if (room.t === 'd') { + RocketChat.models.Subscriptions.removeByRoomId(subscription.rid); + RocketChat.models.Messages.removeByRoomId(subscription.rid); + } + } + }); + + RocketChat.models.Subscriptions.removeByUserId(Meteor.userId()); // Remove user subscriptions + RocketChat.models.Rooms.removeByTypeContainingUsername('d', user.username); // Remove direct rooms with the user + RocketChat.models.Rooms.removeUsernameFromAll(user.username); // Remove user from all other rooms + RocketChat.models.Users.removeById(Meteor.userId()); // Remove user from users database + + return true; + } +}) \ No newline at end of file diff --git a/packages/rocketchat-theme/assets/stylesheets/base.less b/packages/rocketchat-theme/assets/stylesheets/base.less index e2479fb7711..2a98c628a77 100644 --- a/packages/rocketchat-theme/assets/stylesheets/base.less +++ b/packages/rocketchat-theme/assets/stylesheets/base.less @@ -27,6 +27,10 @@ user-select: text; } +.text-right { + text-align: right; +} + .no-scroll { overflow: hidden !important; } diff --git a/packages/rocketchat-ui-account/account/accountPreferences.coffee b/packages/rocketchat-ui-account/account/accountPreferences.coffee index 575c1cd42ba..6684f4df069 100644 --- a/packages/rocketchat-ui-account/account/accountPreferences.coffee +++ b/packages/rocketchat-ui-account/account/accountPreferences.coffee @@ -84,3 +84,28 @@ Template.accountPreferences.events username: 'rocket.cat' title: TAPi18n.__('Desktop_Notification_Test') text: TAPi18n.__('This_is_a_desktop_notification') + + 'click .delete-account button': (e) -> + e.preventDefault(); + + swal + title: t("Are_you_sure_you_want_to_delete_your_account"), + text: t("If_you_are_sure_type_in_your_password"), + type: "input", + inputType: "password", + showCancelButton: true, + closeOnConfirm: false + + , (typedPassword) => + if typedPassword + toastr.warning(t("Please_wait_while_your_account_is_being_deleted")); + Meteor.call 'deleteUserOwnAccount', SHA256(typedPassword), (error, results) -> + if error + toastr.remove(); + swal.showInputError(t("Your_password_is_wrong")); + else + swal.close(); + toastr.remove(); + else + swal.showInputError(t("You_need_to_type_in_your_password_in_order_to_do_this")); + return false; diff --git a/packages/rocketchat-ui-account/account/accountPreferences.html b/packages/rocketchat-ui-account/account/accountPreferences.html index c768eece2c5..2325775dadd 100644 --- a/packages/rocketchat-ui-account/account/accountPreferences.html +++ b/packages/rocketchat-ui-account/account/accountPreferences.html @@ -115,6 +115,9 @@
+
+ +
diff --git a/packages/rocketchat-ui-account/package.js b/packages/rocketchat-ui-account/package.js index 5e818f1b6b8..1c73d18d6b4 100644 --- a/packages/rocketchat-ui-account/package.js +++ b/packages/rocketchat-ui-account/package.js @@ -18,7 +18,8 @@ Package.onUse(function(api) { 'templating', 'coffeescript', 'underscore', - 'rocketchat:lib' + 'rocketchat:lib', + 'sha' ]); api.addFiles('account/account.html', 'client'); From 1f23830a5ee5c1f5174fed8b792a62cdd4fe296b Mon Sep 17 00:00:00 2001 From: Marcelo Schmidt Date: Wed, 2 Mar 2016 17:42:47 -0300 Subject: [PATCH 2/4] Defers user deletion --- .../server/methods/deleteUserOwnAccount.js | 37 ++++++++++--------- .../account/accountPreferences.coffee | 1 - 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/packages/rocketchat-lib/server/methods/deleteUserOwnAccount.js b/packages/rocketchat-lib/server/methods/deleteUserOwnAccount.js index 66726978f9f..cf4463f6a72 100644 --- a/packages/rocketchat-lib/server/methods/deleteUserOwnAccount.js +++ b/packages/rocketchat-lib/server/methods/deleteUserOwnAccount.js @@ -4,31 +4,34 @@ Meteor.methods({ throw new Meteor.Error('invalid-user', "[methods] deleteUserOwnAccount -> Invalid user"); } - const user = RocketChat.models.Users.findOneById(Meteor.userId()); + const userId = Meteor.userId(); + const user = RocketChat.models.Users.findOneById(userId); result = Accounts._checkPassword(user, { digest: password, algorithm: 'sha-256' }); if (result.error) { throw new Meteor.Error('invalid-password', "[methods] deleteUserOwnAccount -> Invalid password"); } - RocketChat.models.Messages.removeByUserId(Meteor.userId()); // Remove user messages - RocketChat.models.Subscriptions.findByUserId(Meteor.userId()).forEach((subscription) => { - let room = RocketChat.models.Rooms.findOneById(subscription.rid); - if (room) { - if (room.t !== 'c' && room.usernames.length === 1) { - RocketChat.models.Rooms.removeById(subscription.rid); // Remove non-channel rooms with only 1 user (the one being deleted) + Meteor.defer(function() { + RocketChat.models.Messages.removeByUserId(userId); // Remove user messages + RocketChat.models.Subscriptions.findByUserId(userId).forEach((subscription) => { + let room = RocketChat.models.Rooms.findOneById(subscription.rid); + if (room) { + if (room.t !== 'c' && room.usernames.length === 1) { + RocketChat.models.Rooms.removeById(subscription.rid); // Remove non-channel rooms with only 1 user (the one being deleted) + } + if (room.t === 'd') { + RocketChat.models.Subscriptions.removeByRoomId(subscription.rid); + RocketChat.models.Messages.removeByRoomId(subscription.rid); + } } - if (room.t === 'd') { - RocketChat.models.Subscriptions.removeByRoomId(subscription.rid); - RocketChat.models.Messages.removeByRoomId(subscription.rid); - } - } - }); + }); - RocketChat.models.Subscriptions.removeByUserId(Meteor.userId()); // Remove user subscriptions - RocketChat.models.Rooms.removeByTypeContainingUsername('d', user.username); // Remove direct rooms with the user - RocketChat.models.Rooms.removeUsernameFromAll(user.username); // Remove user from all other rooms - RocketChat.models.Users.removeById(Meteor.userId()); // Remove user from users database + RocketChat.models.Subscriptions.removeByUserId(userId); // Remove user subscriptions + RocketChat.models.Rooms.removeByTypeContainingUsername('d', user.username); // Remove direct rooms with the user + RocketChat.models.Rooms.removeUsernameFromAll(user.username); // Remove user from all other rooms + RocketChat.models.Users.removeById(userId); // Remove user from users database + }); return true; } diff --git a/packages/rocketchat-ui-account/account/accountPreferences.coffee b/packages/rocketchat-ui-account/account/accountPreferences.coffee index 6684f4df069..1c3d41e35bb 100644 --- a/packages/rocketchat-ui-account/account/accountPreferences.coffee +++ b/packages/rocketchat-ui-account/account/accountPreferences.coffee @@ -105,7 +105,6 @@ Template.accountPreferences.events swal.showInputError(t("Your_password_is_wrong")); else swal.close(); - toastr.remove(); else swal.showInputError(t("You_need_to_type_in_your_password_in_order_to_do_this")); return false; From e483b7c3e2fe65b834576f59e8781ecb605bcb2b Mon Sep 17 00:00:00 2001 From: Marcelo Schmidt Date: Thu, 3 Mar 2016 09:22:56 -0300 Subject: [PATCH 3/4] Adds setting to allow/deny own account deletion --- i18n/en.i18n.json | 1 + .../rocketchat-lib/server/methods/deleteUserOwnAccount.js | 4 ++++ packages/rocketchat-lib/server/startup/settings.coffee | 1 + .../rocketchat-ui-account/account/accountPreferences.coffee | 3 +++ .../rocketchat-ui-account/account/accountPreferences.html | 2 ++ 5 files changed, 11 insertions(+) diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json index a568a8a45e0..8a8b26c1c48 100644 --- a/i18n/en.i18n.json +++ b/i18n/en.i18n.json @@ -7,6 +7,7 @@ "Access_Online_Demo" : "Access the Online Demo", "Access_Token_URL" : "Access Token URL", "Accounts" : "Accounts", + "Accounts_AllowDeleteOwnAccount" : "Allow users to delete own account", "Accounts_AllowedDomainsList" : "Allowed Domains List", "Accounts_AllowedDomainsList_Description" : "Comma-separated list of allowed domains", "Accounts_AllowEmailChange" : "Allow E-mail Change", diff --git a/packages/rocketchat-lib/server/methods/deleteUserOwnAccount.js b/packages/rocketchat-lib/server/methods/deleteUserOwnAccount.js index cf4463f6a72..6296b40cec7 100644 --- a/packages/rocketchat-lib/server/methods/deleteUserOwnAccount.js +++ b/packages/rocketchat-lib/server/methods/deleteUserOwnAccount.js @@ -4,6 +4,10 @@ Meteor.methods({ throw new Meteor.Error('invalid-user', "[methods] deleteUserOwnAccount -> Invalid user"); } + if (!RocketChat.settings.get('Accounts_AllowDeleteOwnAccount')) { + throw new Meteor.Error('not-authorized', "[methods] deleteUserOwnAccount -> Not authorized"); + } + const userId = Meteor.userId(); const user = RocketChat.models.Users.findOneById(userId); diff --git a/packages/rocketchat-lib/server/startup/settings.coffee b/packages/rocketchat-lib/server/startup/settings.coffee index d711cb9a2b3..9b86492c354 100644 --- a/packages/rocketchat-lib/server/startup/settings.coffee +++ b/packages/rocketchat-lib/server/startup/settings.coffee @@ -3,6 +3,7 @@ if not RocketChat.models.Settings.findOneById 'uniqueID' RocketChat.models.Settings.createWithIdAndValue 'uniqueID', process.env.DEPLOYMENT_ID or Random.id() RocketChat.settings.addGroup 'Accounts', -> + @add 'Accounts_AllowDeleteOwnAccount', true, { type: 'boolean', public: true } @add 'Accounts_AllowUserProfileChange', true, { type: 'boolean', public: true } @add 'Accounts_AllowUserAvatarChange', true, { type: 'boolean', public: true } @add 'Accounts_AllowUsernameChange', true, { type: 'boolean', public: true } diff --git a/packages/rocketchat-ui-account/account/accountPreferences.coffee b/packages/rocketchat-ui-account/account/accountPreferences.coffee index 1c3d41e35bb..93491c849ca 100644 --- a/packages/rocketchat-ui-account/account/accountPreferences.coffee +++ b/packages/rocketchat-ui-account/account/accountPreferences.coffee @@ -1,4 +1,7 @@ Template.accountPreferences.helpers + allowDeleteOwnAccount: -> + return RocketChat.settings.get('Accounts_AllowDeleteOwnAccount') + checked: (property, value, defaultValue) -> if not Meteor.user()?.settings?.preferences?[property]? and defaultValue is true currentValue = value diff --git a/packages/rocketchat-ui-account/account/accountPreferences.html b/packages/rocketchat-ui-account/account/accountPreferences.html index 2325775dadd..a465c97129c 100644 --- a/packages/rocketchat-ui-account/account/accountPreferences.html +++ b/packages/rocketchat-ui-account/account/accountPreferences.html @@ -115,9 +115,11 @@
+ {{#if allowDeleteOwnAccount}} + {{/if}} From 481f33a06ba7c6631b103a00b02b55b682ed95ca Mon Sep 17 00:00:00 2001 From: Gabriel Engel Date: Thu, 3 Mar 2016 18:57:33 -0300 Subject: [PATCH 4/4] Changed Accounts_AllowDeleteOwnAccount default value to false --- packages/rocketchat-lib/server/startup/settings.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rocketchat-lib/server/startup/settings.coffee b/packages/rocketchat-lib/server/startup/settings.coffee index 9b86492c354..f1b305e1fca 100644 --- a/packages/rocketchat-lib/server/startup/settings.coffee +++ b/packages/rocketchat-lib/server/startup/settings.coffee @@ -3,7 +3,7 @@ if not RocketChat.models.Settings.findOneById 'uniqueID' RocketChat.models.Settings.createWithIdAndValue 'uniqueID', process.env.DEPLOYMENT_ID or Random.id() RocketChat.settings.addGroup 'Accounts', -> - @add 'Accounts_AllowDeleteOwnAccount', true, { type: 'boolean', public: true } + @add 'Accounts_AllowDeleteOwnAccount', false, { type: 'boolean', public: true } @add 'Accounts_AllowUserProfileChange', true, { type: 'boolean', public: true } @add 'Accounts_AllowUserAvatarChange', true, { type: 'boolean', public: true } @add 'Accounts_AllowUsernameChange', true, { type: 'boolean', public: true }