Skip to content

Commit

Permalink
Added option to let user generate password via email
Browse files Browse the repository at this point in the history
Tweaks from feedback and phpstorm hints
  • Loading branch information
sdrenth authored and Mark-H committed Feb 10, 2024
1 parent 73bfd27 commit f20d31e
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 10 deletions.
3 changes: 3 additions & 0 deletions core/lexicon/en/user.inc.php
Expand Up @@ -25,6 +25,7 @@
$_lang['password_gen_specify'] = 'Let me specify the password:';
$_lang['password_method'] = 'Password notification method';
$_lang['password_method_screen'] = 'Show the new password on screen.';
$_lang['password_gen_user_email_specify'] = 'Let the user choose their own password via email';
$_lang['notify_new_user'] = 'Email this user about their new login for this website.';
$_lang['password_new'] = 'New Password';
$_lang['password_notification'] = 'Password Notification';
Expand Down Expand Up @@ -196,3 +197,5 @@
$_lang['users'] = 'Users';
$_lang['user_createdon'] = 'Created On';
$_lang['user_createdon_desc'] = 'The date the user was created.';
$_lang['user_password_email_subject'] = 'Set up your password';
$_lang['user_password_email'] = '<h2>Set up your password</h2><p>We received a request to set up your MODX Revolution password. You can set up your password by clicking the button below and following the instructions on screen.</p><p class="center"><a href="[[+url_scheme]][[+http_host]][[+manager_url]]?modhash=[[+hash]]" class="btn">Set up my password</a></p><p class="small">If you did not send this request, please ignore this email.</p>';
51 changes: 51 additions & 0 deletions core/src/Revolution/Processors/Security/User/Create.php
Expand Up @@ -12,13 +12,16 @@


use Exception;
use MODX\Revolution\Hashing\modHashing;
use MODX\Revolution\Processors\Model\CreateProcessor;
use MODX\Revolution\Processors\Processor;
use MODX\Revolution\modUser;
use MODX\Revolution\modUserGroup;
use MODX\Revolution\modUserGroupMember;
use MODX\Revolution\modUserProfile;
use MODX\Revolution\modX;
use MODX\Revolution\Registry\modRegister;
use MODX\Revolution\Registry\modRegistry;
use MODX\Revolution\Smarty\modSmarty;

/**
Expand Down Expand Up @@ -232,6 +235,54 @@ public function sendNotificationEmail() {
'html' => true,
]);
}

if (
$this->getProperty('passwordgenmethod') === 'user_email_specify'

Check warning on line 240 in core/src/Revolution/Processors/Security/User/Create.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Create.php#L240

Added line #L240 was not covered by tests
) {
$activationHash = bin2hex(random_bytes(32));

Check warning on line 242 in core/src/Revolution/Processors/Security/User/Create.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Create.php#L242

Added line #L242 was not covered by tests

/** @var modRegistry $registry */
$registry = $this->modx->getService('registry', 'registry.modRegistry');

Check warning on line 245 in core/src/Revolution/Processors/Security/User/Create.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Create.php#L245

Added line #L245 was not covered by tests
/** @var modRegister $register */
$register = $registry->getRegister('user', 'registry.modDbRegister');
$register->connect();
$register->subscribe('/pwd/change/');
$register->send('/pwd/change/', [$activationHash => $this->object->get('username')], ['ttl' => 86400]);

Check warning on line 250 in core/src/Revolution/Processors/Security/User/Create.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Create.php#L247-L250

Added lines #L247 - L250 were not covered by tests

// Send activation email
$message = $this->modx->lexicon('user_password_email');
$placeholders = array_merge($this->modx->config, $this->object->toArray());
$placeholders['hash'] = $activationHash;

Check warning on line 255 in core/src/Revolution/Processors/Security/User/Create.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Create.php#L253-L255

Added lines #L253 - L255 were not covered by tests

// Store previous placeholders
$ph = $this->modx->placeholders;

Check warning on line 258 in core/src/Revolution/Processors/Security/User/Create.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Create.php#L258

Added line #L258 was not covered by tests
// now set those useful for modParser
$this->modx->setPlaceholders($placeholders);
$this->modx->getParser()->processElementTags('', $message, true, false, '[[', ']]', [], 10);
$this->modx->getParser()->processElementTags('', $message, true, true, '[[', ']]', [], 10);

Check warning on line 262 in core/src/Revolution/Processors/Security/User/Create.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Create.php#L260-L262

Added lines #L260 - L262 were not covered by tests
// Then restore previous placeholders to prevent any breakage
$this->modx->placeholders = $ph;

Check warning on line 264 in core/src/Revolution/Processors/Security/User/Create.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Create.php#L264

Added line #L264 was not covered by tests

$this->modx->getService('smarty', 'smarty.modSmarty', '', ['template_dir' => $this->modx->getOption('manager_path') . 'templates/default/']);

Check warning on line 266 in core/src/Revolution/Processors/Security/User/Create.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Create.php#L266

Added line #L266 was not covered by tests

$this->modx->smarty->assign('_config', $this->modx->config);
$this->modx->smarty->assign('content', $message, true);

Check warning on line 269 in core/src/Revolution/Processors/Security/User/Create.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Create.php#L268-L269

Added lines #L268 - L269 were not covered by tests

$sent = $this->object->sendEmail(
$this->modx->smarty->fetch('email/default.tpl'),
[
'from' => $this->modx->getOption('emailsender'),
'fromName' => $this->modx->getOption('site_name'),
'sender' => $this->modx->getOption('emailsender'),
'subject' => $this->modx->lexicon('user_password_email_subject'),
'html' => true,
]
);

Check warning on line 280 in core/src/Revolution/Processors/Security/User/Create.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Create.php#L271-L280

Added lines #L271 - L280 were not covered by tests

if (!$sent) {
return $this->failure($this->modx->lexicon('error_sending_email_to') . $this->object->get('email'));

Check warning on line 283 in core/src/Revolution/Processors/Security/User/Create.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Create.php#L282-L283

Added lines #L282 - L283 were not covered by tests
}
}
}

/**
Expand Down
82 changes: 74 additions & 8 deletions core/src/Revolution/Processors/Security/User/Update.php
Expand Up @@ -11,6 +11,7 @@
namespace MODX\Revolution\Processors\Security\User;


use MODX\Revolution\Hashing\modHashing;
use MODX\Revolution\Processors\Model\UpdateProcessor;
use MODX\Revolution\Processors\Processor;
use MODX\Revolution\modSystemEvent;
Expand All @@ -19,6 +20,8 @@
use MODX\Revolution\modUserGroupMember;
use MODX\Revolution\modUserProfile;
use MODX\Revolution\modX;
use MODX\Revolution\Registry\modRegister;
use MODX\Revolution\Registry\modRegistry;

/**
* Update a user.
Expand Down Expand Up @@ -285,6 +288,7 @@ public function setUserGroups() {
*/
public function afterSave() {
$this->setUserGroups();
$this->sendNotificationEmail();

Check warning on line 291 in core/src/Revolution/Processors/Security/User/Update.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Update.php#L291

Added line #L291 was not covered by tests
if ($this->activeStatusChanged) {
$this->fireAfterActiveStatusChange();
}
Expand All @@ -305,6 +309,62 @@ public function fireAfterActiveStatusChange() {
);
}

/**
* Send the password notification email, if specified
*
* @return void
* @throws Exception
*/
public function sendNotificationEmail() {
if ($this->getProperty('passwordgenmethod') === 'user_email_specify') {
$activationHash = bin2hex(random_bytes(32));

Check warning on line 320 in core/src/Revolution/Processors/Security/User/Update.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Update.php#L318-L320

Added lines #L318 - L320 were not covered by tests

/** @var modRegistry $registry */
$registry = $this->modx->getService('registry', 'registry.modRegistry');

Check warning on line 323 in core/src/Revolution/Processors/Security/User/Update.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Update.php#L323

Added line #L323 was not covered by tests
/** @var modRegister $register */
$register = $registry->getRegister('user', 'registry.modDbRegister');
$register->connect();
$register->subscribe('/pwd/change/');
$register->send('/pwd/change/', [$activationHash => $this->object->get('username')], ['ttl' => 86400]);

Check warning on line 328 in core/src/Revolution/Processors/Security/User/Update.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Update.php#L325-L328

Added lines #L325 - L328 were not covered by tests

$this->modx->lexicon->load('core:login');

Check warning on line 330 in core/src/Revolution/Processors/Security/User/Update.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Update.php#L330

Added line #L330 was not covered by tests

// Send activation email
$message = $this->modx->lexicon('user_password_email');
$placeholders = array_merge($this->modx->config, $this->object->toArray());
$placeholders['hash'] = $activationHash;

Check warning on line 335 in core/src/Revolution/Processors/Security/User/Update.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Update.php#L333-L335

Added lines #L333 - L335 were not covered by tests

// Store previous placeholders
$ph = $this->modx->placeholders;

Check warning on line 338 in core/src/Revolution/Processors/Security/User/Update.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Update.php#L338

Added line #L338 was not covered by tests
// now set those useful for modParser
$this->modx->setPlaceholders($placeholders);
$this->modx->getParser()->processElementTags('', $message, true, false, '[[', ']]', [], 10);
$this->modx->getParser()->processElementTags('', $message, true, true, '[[', ']]', [], 10);

Check warning on line 342 in core/src/Revolution/Processors/Security/User/Update.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Update.php#L340-L342

Added lines #L340 - L342 were not covered by tests
// Then restore previous placeholders to prevent any breakage
$this->modx->placeholders = $ph;

Check warning on line 344 in core/src/Revolution/Processors/Security/User/Update.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Update.php#L344

Added line #L344 was not covered by tests

$this->modx->getService('smarty', 'smarty.modSmarty', '', ['template_dir' => $this->modx->getOption('manager_path') . 'templates/default/']);

Check warning on line 346 in core/src/Revolution/Processors/Security/User/Update.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Update.php#L346

Added line #L346 was not covered by tests

$this->modx->smarty->assign('_config', $this->modx->config);
$this->modx->smarty->assign('content', $message, true);

Check warning on line 349 in core/src/Revolution/Processors/Security/User/Update.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Update.php#L348-L349

Added lines #L348 - L349 were not covered by tests

$sent = $this->object->sendEmail(
$this->modx->smarty->fetch('email/default.tpl'),
[
'from' => $this->modx->getOption('emailsender'),
'fromName' => $this->modx->getOption('site_name'),
'sender' => $this->modx->getOption('emailsender'),
'subject' => $this->modx->lexicon('user_password_email_subject'),
'html' => true,
]
);

Check warning on line 360 in core/src/Revolution/Processors/Security/User/Update.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Update.php#L351-L360

Added lines #L351 - L360 were not covered by tests

if (!$sent) {
return $this->failure($this->modx->lexicon('error_sending_email_to') . $this->object->get('email'));

Check warning on line 363 in core/src/Revolution/Processors/Security/User/Update.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Update.php#L362-L363

Added lines #L362 - L363 were not covered by tests
}
}
}

/**
* {@inheritDoc}
* @return array|string
Expand All @@ -319,14 +379,20 @@ public function cleanup()
unset($userArray['password'], $userArray['cachepwd'], $userArray['sessionid'], $userArray['salt']);

$passwordNotifyMethod = $this->getProperty('passwordnotifymethod');
if (!empty($passwordNotifyMethod) && !empty($this->newPassword) && $passwordNotifyMethod == 's') {
return $this->success($this->modx->lexicon('user_updated_password_message',
[
'password' => $this->newPassword,
]
), $this->object);
} else {
return $this->success('',$this->object);
if (
!empty($passwordNotifyMethod)
&& !empty($this->newPassword)
&& $passwordNotifyMethod === 's'

Check warning on line 385 in core/src/Revolution/Processors/Security/User/Update.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Update.php#L383-L385

Added lines #L383 - L385 were not covered by tests
) {
return $this->success(
$this->modx->lexicon('user_updated_password_message',
array(
'password' => $this->newPassword,
)
),
$this->object
);

Check warning on line 394 in core/src/Revolution/Processors/Security/User/Update.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Update.php#L387-L394

Added lines #L387 - L394 were not covered by tests
}
return $this->success('', $this->object);

Check warning on line 396 in core/src/Revolution/Processors/Security/User/Update.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Update.php#L396

Added line #L396 was not covered by tests
}
}
6 changes: 4 additions & 2 deletions core/src/Revolution/Processors/Security/User/Validation.php
Expand Up @@ -76,12 +76,14 @@ public function alreadyExists($name) {
public function checkPassword() {
$newPassword = $this->processor->getProperty('newpassword',null);
$id = $this->processor->getProperty('id');
if ($newPassword !== null && $newPassword != 'false' || empty($id)) {

$passwordGenerationMethod = $this->processor->getProperty('passwordgenmethod','g');
if ($passwordGenerationMethod !== 'user_email_specify' && ($newPassword !== null && $newPassword != 'false' || empty($id))) {

Check warning on line 81 in core/src/Revolution/Processors/Security/User/Validation.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Security/User/Validation.php#L80-L81

Added lines #L80 - L81 were not covered by tests
$passwordNotifyMethod = $this->processor->getProperty('passwordnotifymethod',null);
if (empty($passwordNotifyMethod)) {
$this->processor->addFieldError('password_notify_method',$this->modx->lexicon('user_err_not_specified_notification_method'));
}
$passwordGenerationMethod = $this->processor->getProperty('passwordgenmethod','g');

if ($passwordGenerationMethod == 'g') {
$autoPassword = $this->user->generatePassword();
$this->user->set('password', $autoPassword);
Expand Down
7 changes: 7 additions & 0 deletions manager/assets/modext/widgets/security/modx.panel.user.js
Expand Up @@ -483,6 +483,13 @@ Ext.extend(MODx.panel.User,MODx.FormPanel,{
,xtype: 'radio'
,inputValue: 'spec'
,value: 'spec'
},{
id: 'modx-user-password-genmethod-user-email-specify'
,name: 'passwordgenmethod'
,boxLabel: _('password_gen_user_email_specify')
,xtype: 'radio'
,inputValue: 'user_email_specify'
,value: 'user_email_specify'
}]
},{
id: 'modx-user-panel-newpassword'
Expand Down

0 comments on commit f20d31e

Please sign in to comment.