[NEW] Option for admins to set a random password to a user (#15818)

pull/15442/head
pierre-lehnen-rc 6 years ago committed by Diego Sampaio
parent fbf6b12500
commit 08273a7ae1
  1. 1
      app/api/server/v1/users.js
  2. 77
      app/lib/server/functions/saveUser.js
  3. 15
      app/lib/server/startup/email.js
  4. 23
      app/ui-flextab/client/tabs/userEdit.html
  5. 17
      app/ui-flextab/client/tabs/userEdit.js
  6. 7
      packages/rocketchat-i18n/i18n/en.i18n.json

@ -31,6 +31,7 @@ API.v1.addRoute('users.create', { authRequired: true }, {
roles: Match.Maybe(Array),
joinDefaultChannels: Match.Maybe(Boolean),
requirePasswordChange: Match.Maybe(Boolean),
setRandomPassword: Match.Maybe(Boolean),
sendWelcomeEmail: Match.Maybe(Boolean),
verified: Match.Maybe(Boolean),
customFields: Match.Maybe(Object),

@ -3,6 +3,7 @@ import { Accounts } from 'meteor/accounts-base';
import _ from 'underscore';
import s from 'underscore.string';
import { Gravatar } from 'meteor/jparker:gravatar';
import { Random } from 'meteor/random';
import * as Mailer from '../../../mailer';
import { getRoles, hasPermission } from '../../../authorization';
@ -13,12 +14,43 @@ import { validateEmailDomain } from '../lib';
import { checkEmailAvailability, checkUsernameAvailability, setUserAvatar, setEmail, setRealName, setUsername, setStatusText } from '.';
let html = '';
let passwordChangedHtml = '';
Meteor.startup(() => {
Mailer.getTemplate('Accounts_UserAddedEmail_Email', (template) => {
html = template;
});
Mailer.getTemplate('Password_Changed_Email', (template) => {
passwordChangedHtml = template;
});
});
function _sendUserEmail(subject, html, userData) {
const email = {
to: userData.email,
from: settings.get('From_Email'),
subject,
html,
data: {
email: s.escapeHTML(userData.email),
password: s.escapeHTML(userData.password),
},
};
if (typeof userData.name !== 'undefined') {
email.data.name = s.escapeHTML(userData.name);
}
try {
Mailer.send(email);
} catch (error) {
throw new Meteor.Error('error-email-send-failed', `Error trying to send email: ${ error.message }`, {
function: 'RocketChat.saveUser',
message: error.message,
});
}
}
function validateUserData(userId, userData) {
const existingRoles = _.pluck(getRoles(), '_id');
@ -80,7 +112,7 @@ function validateUserData(userId, userData) {
});
}
if (!userData._id && !userData.password) {
if (!userData._id && !userData.password && !userData.setRandomPassword) {
throw new Meteor.Error('error-the-field-is-required', 'The field Password is required', {
method: 'insertOrUpdateUser',
field: 'Password',
@ -162,6 +194,17 @@ function validateUserEditing(userId, userData) {
export const saveUser = function(userId, userData) {
validateUserData(userId, userData);
let sendPassword = false;
if (userData.hasOwnProperty('setRandomPassword')) {
if (userData.setRandomPassword) {
userData.password = Random.id();
userData.requirePasswordChange = true;
sendPassword = true;
}
delete userData.setRandomPassword;
}
if (!userData._id) {
validateEmailDomain(userData.email);
@ -200,31 +243,7 @@ export const saveUser = function(userId, userData) {
Meteor.users.update({ _id }, updateUser);
if (userData.sendWelcomeEmail) {
const subject = settings.get('Accounts_UserAddedEmail_Subject');
const email = {
to: userData.email,
from: settings.get('From_Email'),
subject,
html,
data: {
email: s.escapeHTML(userData.email),
password: s.escapeHTML(userData.password),
},
};
if (typeof userData.name !== 'undefined') {
email.data.name = s.escapeHTML(userData.name);
}
try {
Mailer.send(email);
} catch (error) {
throw new Meteor.Error('error-email-send-failed', `Error trying to send email: ${ error.message }`, {
function: 'RocketChat.saveUser',
message: error.message,
});
}
_sendUserEmail(settings.get('Accounts_UserAddedEmail_Subject'), html, userData);
}
userData._id = _id;
@ -264,6 +283,8 @@ export const saveUser = function(userId, userData) {
if (userData.password && userData.password.trim() && hasPermission(userId, 'edit-other-user-password') && passwordPolicy.validate(userData.password)) {
Accounts.setPassword(userData._id, userData.password.trim());
} else {
sendPassword = false;
}
const updateUser = {
@ -291,5 +312,9 @@ export const saveUser = function(userId, userData) {
Meteor.users.update({ _id: userData._id }, updateUser);
if (sendPassword) {
_sendUserEmail(settings.get('Password_Changed_Email_Subject'), passwordChangedHtml, userData);
}
return true;
};

@ -443,6 +443,21 @@ settings.addGroup('Email', function() {
});
});
this.section('Password_changed_section', function() {
this.add('Password_Changed_Email_Subject', '{Password_Changed_Email_Subject}', {
type: 'string',
i18nLabel: 'Subject',
});
this.add('Password_Changed_Email', '<h2>{Hi},</h2><p>{Your_password_was_changed_by_an_admin}</p><p>{Your_temporary_password_is_password}</p><a class="btn" target="_blank" href="[Site_URL]">{Login}</a>', {
type: 'code',
code: 'text/html',
multiline: true,
i18nLabel: 'Body',
i18nDescription: 'Password_Changed_Description',
});
});
this.section('Privacy', function() {
this.add('Email_notification_show_message', true, {
type: 'boolean',

@ -114,19 +114,16 @@
{{#if hasPermission 'edit-other-user-password'}}
<div class="rc-form-group rc-form-group--small rc-form-group--inline">
<div class="rc-input rc-form-item-inline">
<label class="rc-input__label">
<div class="rc-input__title">{{_ "Password"}}</div>
<div class="rc-input__wrapper">
<div class="rc-input__icon">
{{> icon icon='key' }}
</div>
<input name="password" class="rc-input__element rc-input__element--small" type="password" id="password" autocomplete="off" value=""/>
</div>
</label>
</div>
<button id="randomPassword" class="rc-button rc-button--primary rc-form-item-inline">{{_ 'Random'}}</button>
<div class="rc-form-group rc-form-group--small rc-switch">
<label class="rc-switch__label" tabindex="-1">
<input class="rc-switch__input" type="checkbox" id="setRandomPassword" value="1" checked="{{setRandomPassword}}"/>
<span class="rc-switch__button">
<span class="rc-switch__button-inside"></span>
</span>
<span class="rc-switch__text">
{{_ "Set_random_password"}}
</span>
</label>
</div>
<div class="rc-form-group rc-form-group--small rc-switch">
<label class="rc-switch__label" tabindex="-1">

@ -1,6 +1,5 @@
import { Meteor } from 'meteor/meteor';
import { ReactiveVar } from 'meteor/reactive-var';
import { Random } from 'meteor/random';
import { Template } from 'meteor/templating';
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
import toastr from 'toastr';
@ -43,6 +42,10 @@ Template.userEdit.helpers({
return !Template.instance().user || Template.instance().user.requirePasswordChange;
},
setRandomPassword() {
return !Template.instance().user || Template.instance().user.setRandomPassword;
},
role() {
const roles = Template.instance().roles.get();
return Roles.find({ _id: { $nin: roles }, scope: 'Users' }, { sort: { description: 1, _id: 1 } });
@ -122,17 +125,6 @@ Template.userEdit.events({
$(`[title=${ this }]`).remove();
},
'click #randomPassword'(e) {
e.stopPropagation();
e.preventDefault();
e.target.classList.add('loading');
$('#password').val('');
setTimeout(() => {
$('#password').val(Random.id());
e.target.classList.remove('loading');
}, 1000);
},
'mouseover #password'(e) {
e.target.type = 'text';
},
@ -186,6 +178,7 @@ Template.userEdit.onCreated(function() {
userData.email = s.trim(this.$('#email').val());
userData.verified = this.$('#verified:checked').length > 0;
userData.password = s.trim(this.$('#password').val());
userData.setRandomPassword = this.$('#setRandomPassword:checked').length > 0;
userData.requirePasswordChange = this.$('#changePassword:checked').length > 0;
userData.joinDefaultChannels = this.$('#joinDefaultChannels:checked').length > 0;
userData.sendWelcomeEmail = this.$('#sendWelcomeEmail:checked').length > 0;

@ -1564,6 +1564,7 @@
"Help_Center": "Help Center",
"Helpers": "Helpers",
"Hex_Color_Preview": "Hex Color Preview",
"Hi": "Hi",
"Hi_username": "Hi __name__",
"Hidden": "Hidden",
"Hide": "Hide",
@ -2417,6 +2418,9 @@
"Parent_channel_doesnt_exist": "Channel does not exist.",
"Password": "Password",
"Password_Change_Disabled": "Your Rocket.Chat administrator has disabled the changing of passwords",
"Password_Changed_Description": "You may use the following placeholders: <br/><ul><li>[password] for the temporary password.</li><li>[name], [fname], [lname] for the user's full name, first name or last name, respectively.</li><li>[email] for the user's email.</li><li>[Site_Name] and [Site_URL] for the Application Name and URL respectively.</li></ul>",
"Password_Changed_Email_Subject": "[Site_Name] - Password Changed",
"Password_changed_section": "Password Changed",
"Password_changed_successfully": "Password changed successfully",
"Password_Policy": "Password Policy",
"Past_Chats": "Past Chats",
@ -2816,6 +2820,7 @@
"set-moderator_description": "Permission to set other users as moderator of a channel",
"set-owner": "Set Owner",
"set-owner_description": "Permission to set other users as owner of a channel",
"Set_random_password": "Set random password",
"set-react-when-readonly": "Set React When ReadOnly",
"set-react-when-readonly_description": "Permission to set the ability to react to messages in a read only channel",
"set-readonly": "Set ReadOnly",
@ -3469,8 +3474,10 @@
"your_message_optional": "your message (optional)",
"Your_new_email_is_email": "Your new email address is <strong>[email]</strong>.",
"Your_password_is_wrong": "Your password is wrong!",
"Your_password_was_changed_by_an_admin": "Your password was changed by an admin.",
"Your_push_was_sent_to_s_devices": "Your push was sent to %s devices",
"Your_question": "Your question",
"Your_server_link": "Your server link",
"Your_temporary_password_is_password": "Your temporary password is <strong>[password]</strong>.",
"Your_workspace_is_ready": "Your workspace is ready to use 🎉"
}

Loading…
Cancel
Save