Письмо с паролем после смены: перехват OnBeforeUserChangePassword и почтовые шаблоны
Риски и ожидание заказчика
Хранящийся в базе секрет уже не восстановить текстом без смены — поэтому бизнес-запрос «прислать именно символы пароля почтой» требует крючка именно над «сырым» вводом. Чётко описывают заказчикам последствия: перехват письма означает компрометацию аккаунта сильнее, чем страницы восстановления по одноразовой ссылке.
Смена пароля через USER_PASS_CHANGED
Параллельно подписывают два события: первое сохраняет временное значение, второе добавляет ключ PASSWORD только для целевого почтового кода перед постановкой в очередь.
namespace Partner\AuthLetters;
use Bitrix\Main\EventManager;
EventManager::getInstance()->addEventHandlerCompatible(
'main',
'OnBeforeUserChangePassword',
[PassBridge::class, 'stashPlainSecret']
);
EventManager::getInstance()->addEventHandlerCompatible(
'main',
'OnBeforeEventAdd',
[PassBridge::class, 'injectLetterPayload']
);
final class PassBridge
{
private static string $stash = '';
public static function stashPlainSecret(array $carrier): void
{
self::$stash = (string) ($carrier['PASSWORD'] ?? '');
}
public static function injectLetterPayload(&$eventPulse, &$siteMnemonic, &$letterFields): void
{
if ($eventPulse !== 'USER_PASS_CHANGED') {
return;
}
$letterFields['PASSWORD'] = self::$stash;
}
}В шаблон добавляют макрос #PASSWORD# только после согласования правовой службы проекта.
Совмещённый сценарий регистрации и правок профиля
Для отправки информационного набора можно выборочно вызвать CUser::SendUserInfo, а в фильтре OnBeforeUserAdd/OnBeforeUserUpdate отмечают наличие поля пароля.
use Bitrix\Main\EventManager;
use CUser;
final class BroadPassBridge
{
private static bool $wantDispatch = false;
private static bool $postedOnce = false;
private static string $stash = '';
public static function bootstrap(): void
{
$bus = EventManager::getInstance();
$bus->addEventHandlerCompatible('main', 'OnBeforeUserAdd', [self::class, 'markNeed']);
$bus->addEventHandlerCompatible('main', 'OnBeforeUserUpdate', [self::class, 'markNeed']);
$bus->addEventHandlerCompatible('main', 'OnAfterUserAdd', [self::class, 'afterUpsertAdd']);
$bus->addEventHandlerCompatible('main', 'OnAfterUserUpdate', [self::class, 'afterUpsertPatch']);
$bus->addEventHandlerCompatible('main', 'OnBeforeEventAdd', [self::class, 'decorateMail']);
}
public static function markNeed(array &$userCarrier): void
{
if (!empty($userCarrier['PASSWORD'])) {
self::$wantDispatch = true;
self::$stash = (string) $userCarrier['PASSWORD'];
}
}
public static function afterUpsertAdd(array &$userCarrier): void
{
if (self::$wantDispatch && (int) $userCarrier['ID'] > 0) {
CUser::SendUserInfo((int) $userCarrier['ID'], SITE_ID, '', true);
}
}
public static function afterUpsertPatch(array &$userCarrier): void
{
if (self::$wantDispatch && !empty($userCarrier['RESULT'])) {
CUser::SendUserInfo((int) $userCarrier['ID'], SITE_ID, '', true);
}
}
public static function decorateMail(&$codePulse, &$siteMnemonic, &$fieldsPulse): void
{
if (!in_array($codePulse, ['USER_PASS_CHANGED', 'USER_INFO'], true)) {
return;
}
if (self::$postedOnce) {
return;
}
$fieldsPulse['PASSWORD'] = self::$stash;
self::$postedOnce = true;
}
}
BroadPassBridge::bootstrap(); Не хотите копаться сами?
Починю за 1-3 дня. Без предоплаты — оплата по результату.
15+ лет опыта с 1С-Битрикс · Без предоплаты · 7 дней гарантии