Merge pull request #15551 from annando/lostpass

"lostpass" is now a module as well
This commit is contained in:
Philipp 2026-03-06 07:19:17 +01:00 committed by GitHub
commit 2b596087ab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 372 additions and 307 deletions

View file

@ -35,7 +35,7 @@ jobs:
tools: none
- name: Clone addon repository
run: git clone -b 2025.07-rc --single-branch https://git.friendi.ca/friendica/friendica-addons.git addon
run: git clone -b develop --single-branch https://git.friendi.ca/friendica/friendica-addons.git addon
- name: Install Composer dependencies
uses: "ramsey/composer-install@v2"
@ -68,7 +68,7 @@ jobs:
tools: none
- name: Clone addon repository
run: git clone -b 2025.07-rc --single-branch https://git.friendi.ca/friendica/friendica-addons.git addon
run: git clone -b develop --single-branch https://git.friendi.ca/friendica/friendica-addons.git addon
- name: Install Composer dependencies
uses: "ramsey/composer-install@v2"
@ -100,7 +100,7 @@ jobs:
tools: none
- name: Clone addon repository
run: git clone -b 2025.07-rc --single-branch https://git.friendi.ca/friendica/friendica-addons.git addon
run: git clone -b develop --single-branch https://git.friendi.ca/friendica/friendica-addons.git addon
- name: Install Composer dependencies
uses: "ramsey/composer-install@v2"
@ -132,7 +132,7 @@ jobs:
tools: none
- name: Clone addon repository
run: git clone -b 2025.07-rc --single-branch https://git.friendi.ca/friendica/friendica-addons.git addon
run: git clone -b develop --single-branch https://git.friendi.ca/friendica/friendica-addons.git addon
- name: Install Composer dependencies
uses: "ramsey/composer-install@v2"

View file

@ -34,7 +34,7 @@ jobs:
tools: none
- name: Clone addon repository
run: git clone -b 2025.07-rc --single-branch https://git.friendi.ca/friendica/friendica-addons.git addon
run: git clone -b develop --single-branch https://git.friendi.ca/friendica/friendica-addons.git addon
- name: Install Composer dependencies
uses: "ramsey/composer-install@v2"
@ -88,7 +88,7 @@ jobs:
ini-values: apc.enabled=1, apc.enable_cli=1
- name: Clone addon repository
run: git clone -b 2025.07-rc --single-branch https://git.friendi.ca/friendica/friendica-addons.git addon
run: git clone -b develop --single-branch https://git.friendi.ca/friendica/friendica-addons.git addon
# Install composer dependencies and handle caching in one go.
# @link https://github.com/marketplace/actions/install-php-dependencies-with-composer

View file

@ -57,7 +57,7 @@ steps:
composer_install:
image: friendicaci/php${PHP_MAJOR_VERSION}:php${PHP_VERSION}
commands:
- git clone -b 2025.07-rc --single-branch https://git.friendi.ca/friendica/friendica-addons.git addon
- git clone -b develop --single-branch https://git.friendi.ca/friendica/friendica-addons.git addon
- export COMPOSER_HOME=.composer
- ./bin/composer.phar validate
- ./bin/composer.phar install --prefer-dist

View file

@ -1,172 +0,0 @@
<?php
/**
* Copyright (C) 2010-2024, the Friendica project
* SPDX-FileCopyrightText: 2010-2024 the Friendica project
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*
*/
use Friendica\Core\Renderer;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model\User;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Strings;
function lostpass_post()
{
$loginame = trim($_POST['login-name']);
if (!$loginame) {
DI::baseUrl()->redirect();
}
$condition = ['(`email` = ? OR `nickname` = ?) AND `verified` AND NOT `blocked` AND NOT `account_removed` AND NOT `account_expired`', $loginame, $loginame];
$user = DBA::selectFirst('user', ['uid', 'username', 'nickname', 'email', 'language'], $condition);
if (!DBA::isResult($user)) {
DI::sysmsg()->addNotice(DI::l10n()->t('No valid account found.'));
DI::baseUrl()->redirect();
}
$pwdreset_token = Strings::getRandomHex(32);
$fields = [
'pwdreset' => hash('sha256', $pwdreset_token),
'pwdreset_time' => DateTimeFormat::utcNow()
];
$result = DBA::update('user', $fields, ['uid' => $user['uid']]);
if ($result) {
DI::sysmsg()->addInfo(DI::l10n()->t('Password reset request issued. Check your email.'));
}
$sitename = DI::config()->get('config', 'sitename');
$resetlink = DI::baseUrl() . '/lostpass/' . $pwdreset_token;
$preamble = Strings::deindent(DI::l10n()->t('
Dear %1$s,
A request was recently received at "%2$s" to reset your account
password. In order to confirm this request, please select the verification link
below or paste it into your web browser address bar.
If you did NOT request this change, please DO NOT follow the link
provided and ignore and/or delete this email, the request will expire shortly.
Your password will not be changed unless we can verify that you
issued this request.', $user['username'], $sitename));
$body = Strings::deindent(DI::l10n()->t('
Follow this link soon to verify your identity:
%1$s
You will then receive a follow-up message containing the new password.
You may change that password from your account settings page after logging in.
The login details are as follows:
Site Location: %2$s
Login Name: %3$s', $resetlink, DI::baseUrl(), $user['nickname']));
$email = DI::emailer()
->newSystemMail()
->withMessage(DI::l10n()->t('Password reset requested at %s', $sitename), $preamble, $body)
->forUser($user)
->withRecipient($user['email'])
->build();
DI::emailer()->send($email);
DI::baseUrl()->redirect();
}
function lostpass_content()
{
if (DI::args()->getArgc() > 1) {
$pwdreset_token = DI::args()->getArgv()[1];
$user = DBA::selectFirst('user', ['uid', 'username', 'nickname', 'email', 'pwdreset_time', 'language'], ['pwdreset' => hash('sha256', $pwdreset_token)]);
if (!DBA::isResult($user)) {
DI::sysmsg()->addNotice(DI::l10n()->t("Request could not be verified. \x28You may have previously submitted it.\x29 Password reset failed."));
return lostpass_form();
}
// Password reset requests expire in 60 minutes
if ($user['pwdreset_time'] < DateTimeFormat::utc('now - 1 hour')) {
$fields = [
'pwdreset' => null,
'pwdreset_time' => null
];
DBA::update('user', $fields, ['uid' => $user['uid']]);
DI::sysmsg()->addNotice(DI::l10n()->t('Request has expired, please make a new one.'));
return lostpass_form();
}
return lostpass_generate_password($user);
} else {
return lostpass_form();
}
}
function lostpass_form()
{
$tpl = Renderer::getMarkupTemplate('lostpass.tpl');
$o = Renderer::replaceMacros($tpl, [
'$title' => DI::l10n()->t('Forgot your Password?'),
'$desc' => DI::l10n()->t('Enter your email address and submit to have your password reset. Then check your email for further instructions.'),
'$name' => DI::l10n()->t('Nickname or email'),
'$submit' => DI::l10n()->t('Reset my password')
]);
return $o;
}
function lostpass_generate_password($user)
{
$o = '';
$new_password = User::generateNewPassword();
$result = User::updatePassword($user['uid'], $new_password);
if (DBA::isResult($result)) {
$tpl = Renderer::getMarkupTemplate('pwdreset.tpl');
$o .= Renderer::replaceMacros($tpl, [
'$lbl1' => DI::l10n()->t('Password Reset'),
'$lbl2' => DI::l10n()->t('Your password has been reset as requested.'),
'$lbl3' => DI::l10n()->t('Your new password is'),
'$lbl4' => DI::l10n()->t('Save or copy your new password - and then'),
'$lbl5' => '<a href="' . DI::baseUrl() . '">' . DI::l10n()->t('click here to login') . '</a>.',
'$lbl6' => DI::l10n()->t('Your password may be changed from the <em>Settings</em> page after successful login.'),
'$newpass' => $new_password,
]);
DI::sysmsg()->addInfo(DI::l10n()->t("Your password has been reset."));
$sitename = DI::config()->get('config', 'sitename');
$preamble = Strings::deindent(DI::l10n()->t('
Dear %1$s,
Your password has been changed as requested. Please retain this
information for your records ' . "\x28" . 'or change your password immediately to
something that you will remember' . "\x29" . '.
', $user['username']));
$body = Strings::deindent(DI::l10n()->t('
Your login details are as follows:
Site Location: %1$s
Login Name: %2$s
Password: %3$s
You may change that password from your account settings page after logging in.
', DI::baseUrl(), $user['nickname'], $new_password));
$email = DI::emailer()
->newSystemMail()
->withMessage(DI::l10n()->t('Your password has been changed at %s', $sitename), $preamble, $body)
->forUser($user)
->withRecipient($user['email'])
->build();
DI::emailer()->send($email);
}
return $o;
}

236
src/Module/LostPass.php Normal file
View file

@ -0,0 +1,236 @@
<?php
// Copyright (C) 2010-2024, the Friendica project
// SPDX-FileCopyrightText: 2010-2024 the Friendica project
//
// SPDX-License-Identifier: AGPL-3.0-or-later
namespace Friendica\Module;
use Friendica\App\Arguments;
use Friendica\App\BaseURL;
use Friendica\BaseModule;
use Friendica\Core\Renderer;
use Friendica\Database\DBA;
use Friendica\Model\User;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Strings;
use Friendica\Navigation\SystemMessages;
use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Core\L10n;
use Friendica\Util\Emailer;
use Friendica\Util\Profiler;
use Psr\Log\LoggerInterface;
/**
* Lost password module
*
* This module handles password reset functionality for users who have forgotten their passwords.
*/
final class LostPass extends BaseModule
{
private SystemMessages $sysMessages;
private IManageConfigValues $config;
private Emailer $emailer;
/**
* Initialize the module
*
* @param L10n $l10n
* @param BaseURL $baseUrl
* @param Arguments $args
* @param LoggerInterface $logger
* @param Profiler $profiler
* @param Response $response
* @param SystemMessages $sysMessages
* @param IManageConfigValues $config
* @param Emailer $emailer
* @param array $server
* @param array $parameters
*/
public function __construct(L10n $l10n, BaseURL $baseUrl, Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, SystemMessages $sysMessages, IManageConfigValues $config, Emailer $emailer, array $server, array $parameters = [])
{
parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
$this->sysMessages = $sysMessages;
$this->config = $config;
$this->emailer = $emailer;
}
/**
* Handle POST requests for password reset form submission
*
* @param array $request
* @return void
*/
protected function post(array $request = [])
{
$loginame = trim($request['login-name'] ?? '');
if (!$loginame) {
$this->baseUrl->redirect();
}
$condition = ['(`email` = ? OR `nickname` = ?) AND `verified` AND NOT `blocked` AND NOT `account_removed` AND NOT `account_expired`', $loginame, $loginame];
$user = DBA::selectFirst('user', ['uid', 'username', 'nickname', 'email', 'language'], $condition);
if (!DBA::isResult($user)) {
$this->sysMessages->addNotice($this->l10n->t('No valid account found.'));
$this->baseUrl->redirect();
}
$pwdreset_token = Strings::getRandomHex(32);
$fields = [
'pwdreset' => hash('sha256', $pwdreset_token),
'pwdreset_time' => DateTimeFormat::utcNow(),
];
$result = DBA::update('user', $fields, ['uid' => $user['uid']]);
if ($result) {
$this->sysMessages->addInfo($this->l10n->t('Password reset request issued. Check your email.'));
}
$sitename = $this->config->get('config', 'sitename');
$resetlink = $this->baseUrl . '/lostpass/' . $pwdreset_token;
$preamble = Strings::deindent($this->l10n->t('
Dear %1$s,
A request was recently received at "%2$s" to reset your account
password. In order to confirm this request, please select the verification link
below or paste it into your web browser address bar.
If you did NOT request this change, please DO NOT follow the link
provided and ignore and/or delete this email, the request will expire shortly.
Your password will not be changed unless we can verify that you
issued this request.', $user['username'], $sitename));
$body = Strings::deindent($this->l10n->t('
Follow this link soon to verify your identity:
%1$s
You will then receive a follow-up message containing the new password.
You may change that password from your account settings page after logging in.
The login details are as follows:
Site Location: %2$s
Login Name: %3$s', $resetlink, $this->baseUrl, $user['nickname']));
$email = $this->emailer->newSystemMail()
->withMessage($this->l10n->t('Password reset requested at %s', $sitename), $preamble, $body)
->forUser($user)
->withRecipient($user['email'])
->build();
$this->emailer->send($email);
$this->baseUrl->redirect();
}
/**
* Render page content
*
* @param array $request
* @return string Rendered page content
*/
protected function content(array $request = []): string
{
if (isset($this->parameters['token'])) {
$pwdreset_token = $this->parameters['token'];
$user = DBA::selectFirst('user', ['uid', 'username', 'nickname', 'email', 'pwdreset_time', 'language'], ['pwdreset' => hash('sha256', $pwdreset_token)]);
if (!DBA::isResult($user)) {
$this->sysMessages->addNotice($this->l10n->t("Request could not be verified. \x28You may have previously submitted it.\x29 Password reset failed."));
return $this->form();
}
// Password reset requests expire in 60 minutes
if ($user['pwdreset_time'] < DateTimeFormat::utc('now - 1 hour')) {
$fields = [
'pwdreset' => null,
'pwdreset_time' => null,
];
DBA::update('user', $fields, ['uid' => $user['uid']]);
$this->sysMessages->addNotice($this->l10n->t('Request has expired, please make a new one.'));
return $this->form();
}
return $this->generatePassword($user);
} else {
return $this->form();
}
}
/**
* Render the password reset form
*
* @return string Rendered form HTML
*/
private function form(): string
{
$tpl = Renderer::getMarkupTemplate('lostpass.tpl');
$o = Renderer::replaceMacros($tpl, [
'$title' => $this->l10n->t('Forgot your Password?'),
'$desc' => $this->l10n->t('Enter your email address and submit to have your password reset. Then check your email for further instructions.'),
'$name' => $this->l10n->t('Nickname or email'),
'$submit' => $this->l10n->t('Reset my password'),
]);
return $o;
}
/**
* Generate and send a new password to the user
*
* @param array $user User data array
* @return string Rendered success message HTML
*/
private function generatePassword(array $user): string
{
$o = '';
$new_password = User::generateNewPassword();
$result = User::updatePassword($user['uid'], $new_password);
if (DBA::isResult($result)) {
$tpl = Renderer::getMarkupTemplate('pwdreset.tpl');
$o .= Renderer::replaceMacros($tpl, [
'$lbl1' => $this->l10n->t('Password Reset'),
'$lbl2' => $this->l10n->t('Your password has been reset as requested.'),
'$lbl3' => $this->l10n->t('Your new password is'),
'$lbl4' => $this->l10n->t('Save or copy your new password - and then'),
'$lbl5' => '<a href="' . $this->baseUrl . '">' . $this->l10n->t('click here to login') . '</a>.',
'$lbl6' => $this->l10n->t('Your password may be changed from the <em>Settings</em> page after successful login.'),
'$newpass' => $new_password,
]);
$this->sysMessages->addInfo($this->l10n->t("Your password has been reset."));
$sitename = $this->config->get('config', 'sitename');
$preamble = Strings::deindent($this->l10n->t('
Dear %1$s,
Your password has been changed as requested. Please retain this
information for your records ' . "\x28" . 'or change your password immediately to
something that you will remember' . "\x29" . '.
', $user['username']));
$body = Strings::deindent($this->l10n->t('
Your login details are as follows:
Site Location: %1$s
Login Name: %2$s
Password: %3$s
You may change that password from your account settings page after logging in.
', $this->baseUrl, $user['nickname'], $new_password));
$email = $this->emailer->newSystemMail()
->withMessage($this->l10n->t('Your password has been changed at %s', $sitename), $preamble, $body)
->forUser($user)
->withRecipient($user['email'])
->build();
$this->emailer->send($email);
}
return $o;
}
}

View file

@ -479,6 +479,7 @@ return [
'/localtime' => [Module\Debug\Localtime::class, [R::GET, R::POST]],
'/login' => [Module\Security\Login::class, [R::GET, R::POST]],
'/logout' => [Module\Security\Logout::class, [R::GET, R::POST]],
'/lostpass[/{token}]' => [Module\LostPass::class, [R::GET, R::POST]],
'/magic' => [Module\Magic::class, [R::GET]],
'/manifest' => [Module\Manifest::class, [R::GET]],
'/manifest.json' => [Module\Manifest::class, [R::GET]],

View file

@ -82,134 +82,6 @@ msgstr ""
msgid "Permission denied."
msgstr ""
#: mod/lostpass.php:28
msgid "No valid account found."
msgstr ""
#: mod/lostpass.php:40
msgid "Password reset request issued. Check your email."
msgstr ""
#: mod/lostpass.php:46
#, php-format
msgid ""
"\n"
"\t\tDear %1$s,\n"
"\t\t\tA request was recently received at \"%2$s\" to reset your account\n"
"\t\tpassword. In order to confirm this request, please select the verification link\n"
"\t\tbelow or paste it into your web browser address bar.\n"
"\n"
"\t\tIf you did NOT request this change, please DO NOT follow the link\n"
"\t\tprovided and ignore and/or delete this email, the request will expire shortly.\n"
"\n"
"\t\tYour password will not be changed unless we can verify that you\n"
"\t\tissued this request."
msgstr ""
#: mod/lostpass.php:57
#, php-format
msgid ""
"\n"
"\t\tFollow this link soon to verify your identity:\n"
"\n"
"\t\t%1$s\n"
"\n"
"\t\tYou will then receive a follow-up message containing the new password.\n"
"\t\tYou may change that password from your account settings page after logging in.\n"
"\n"
"\t\tThe login details are as follows:\n"
"\n"
"\t\tSite Location:\t%2$s\n"
"\t\tLogin Name:\t%3$s"
msgstr ""
#: mod/lostpass.php:72
#, php-format
msgid "Password reset requested at %s"
msgstr ""
#: mod/lostpass.php:88
msgid "Request could not be verified. (You may have previously submitted it.) Password reset failed."
msgstr ""
#: mod/lostpass.php:101
msgid "Request has expired, please make a new one."
msgstr ""
#: mod/lostpass.php:116
msgid "Forgot your Password?"
msgstr ""
#: mod/lostpass.php:117
msgid "Enter your email address and submit to have your password reset. Then check your email for further instructions."
msgstr ""
#: mod/lostpass.php:118 src/Module/Security/Login.php:167
msgid "Nickname or email"
msgstr ""
#: mod/lostpass.php:119
msgid "Reset my password"
msgstr ""
#: mod/lostpass.php:134 src/Module/Security/Login.php:179
msgid "Password Reset"
msgstr ""
#: mod/lostpass.php:135
msgid "Your password has been reset as requested."
msgstr ""
#: mod/lostpass.php:136
msgid "Your new password is"
msgstr ""
#: mod/lostpass.php:137
msgid "Save or copy your new password - and then"
msgstr ""
#: mod/lostpass.php:138
msgid "click here to login"
msgstr ""
#: mod/lostpass.php:139
msgid "Your password may be changed from the <em>Settings</em> page after successful login."
msgstr ""
#: mod/lostpass.php:143
msgid "Your password has been reset."
msgstr ""
#: mod/lostpass.php:146
#, php-format
msgid ""
"\n"
"\t\t\tDear %1$s,\n"
"\t\t\t\tYour password has been changed as requested. Please retain this\n"
"\t\t\tinformation for your records (or change your password immediately to\n"
"\t\t\tsomething that you will remember).\n"
"\t\t"
msgstr ""
#: mod/lostpass.php:152
#, php-format
msgid ""
"\n"
"\t\t\tYour login details are as follows:\n"
"\n"
"\t\t\tSite Location:\t%1$s\n"
"\t\t\tLogin Name:\t%2$s\n"
"\t\t\tPassword:\t%3$s\n"
"\n"
"\t\t\tYou may change that password from your account settings page after logging in.\n"
"\t\t"
msgstr ""
#: mod/lostpass.php:164
#, php-format
msgid "Your password has been changed at %s"
msgstr ""
#: mod/message.php:28 mod/message.php:124 src/Content/Nav.php:315
#: view/theme/frio/theme.php:229
msgid "Messages"
@ -7237,6 +7109,134 @@ msgstr ""
msgid "Unable to follow this item."
msgstr ""
#: src/Module/LostPass.php:50
msgid "No valid account found."
msgstr ""
#: src/Module/LostPass.php:62
msgid "Password reset request issued. Check your email."
msgstr ""
#: src/Module/LostPass.php:68
#, php-format
msgid ""
"\n"
"\t\t\tDear %1$s,\n"
"\t\t\t\tA request was recently received at \"%2$s\" to reset your account\n"
"\t\t\tpassword. In order to confirm this request, please select the verification link\n"
"\t\t\tbelow or paste it into your web browser address bar.\n"
"\n"
"\t\t\tIf you did NOT request this change, please DO NOT follow the link\n"
"\t\t\tprovided and ignore and/or delete this email, the request will expire shortly.\n"
"\n"
"\t\t\tYour password will not be changed unless we can verify that you\n"
"\t\t\tissued this request."
msgstr ""
#: src/Module/LostPass.php:79
#, php-format
msgid ""
"\n"
"\t\t\tFollow this link soon to verify your identity:\n"
"\n"
"\t\t\t%1$s\n"
"\n"
"\t\t\tYou will then receive a follow-up message containing the new password.\n"
"\t\t\tYou may change that password from your account settings page after logging in.\n"
"\n"
"\t\t\tThe login details are as follows:\n"
"\n"
"\t\t\tSite Location:\t%2$s\n"
"\t\t\tLogin Name:\t%3$s"
msgstr ""
#: src/Module/LostPass.php:93
#, php-format
msgid "Password reset requested at %s"
msgstr ""
#: src/Module/LostPass.php:109
msgid "Request could not be verified. (You may have previously submitted it.) Password reset failed."
msgstr ""
#: src/Module/LostPass.php:122
msgid "Request has expired, please make a new one."
msgstr ""
#: src/Module/LostPass.php:137
msgid "Forgot your Password?"
msgstr ""
#: src/Module/LostPass.php:138
msgid "Enter your email address and submit to have your password reset. Then check your email for further instructions."
msgstr ""
#: src/Module/LostPass.php:139 src/Module/Security/Login.php:167
msgid "Nickname or email"
msgstr ""
#: src/Module/LostPass.php:140
msgid "Reset my password"
msgstr ""
#: src/Module/LostPass.php:155 src/Module/Security/Login.php:179
msgid "Password Reset"
msgstr ""
#: src/Module/LostPass.php:156
msgid "Your password has been reset as requested."
msgstr ""
#: src/Module/LostPass.php:157
msgid "Your new password is"
msgstr ""
#: src/Module/LostPass.php:158
msgid "Save or copy your new password - and then"
msgstr ""
#: src/Module/LostPass.php:159
msgid "click here to login"
msgstr ""
#: src/Module/LostPass.php:160
msgid "Your password may be changed from the <em>Settings</em> page after successful login."
msgstr ""
#: src/Module/LostPass.php:164
msgid "Your password has been reset."
msgstr ""
#: src/Module/LostPass.php:167
#, php-format
msgid ""
"\n"
"\t\t\t\tDear %1$s,\n"
"\t\t\t\t\tYour password has been changed as requested. Please retain this\n"
"\t\t\t\tinformation for your records (or change your password immediately to\n"
"\t\t\t\tsomething that you will remember).\n"
"\t\t\t"
msgstr ""
#: src/Module/LostPass.php:173
#, php-format
msgid ""
"\n"
"\t\t\t\tYour login details are as follows:\n"
"\n"
"\t\t\t\tSite Location:\t%1$s\n"
"\t\t\t\tLogin Name:\t%2$s\n"
"\t\t\t\tPassword:\t%3$s\n"
"\n"
"\t\t\t\tYou may change that password from your account settings page after logging in.\n"
"\t\t\t"
msgstr ""
#: src/Module/LostPass.php:184
#, php-format
msgid "Your password has been changed at %s"
msgstr ""
#: src/Module/Maintenance.php:34 src/Module/Maintenance.php:39
msgid "System down for maintenance"
msgstr ""