From fa6c7e77bc0e3d9130ffe636b4291eaaaa318fad Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 23 Jan 2026 13:58:57 +0100 Subject: [PATCH] Refactor events for #11544 --- .../User/UserLoggedInFromNewIpAddress.php} | 15 +- .../User/UserSuccessfullyLoggedIn.php} | 17 +- ....php => OwnerTestsNotificationChannel.php} | 4 +- ...l.php => UserTestsNotificationChannel.php} | 2 +- app/Handlers/Events/AdminEventHandler.php | 57 +----- app/Handlers/Events/UserEventHandler.php | 180 +----------------- .../Admin/NotificationController.php | 4 +- app/Http/Controllers/Auth/LoginController.php | 35 ++-- .../Controllers/PreferencesController.php | 4 +- .../User/NotifiesUserAboutNewIpAddress.php | 71 +++++++ .../Security/User/StoresNewIpAddress.php | 81 ++++++++ app/Listeners/Test/SendsTestNotification.php | 89 +++++++++ app/Providers/EventServiceProvider.php | 25 +-- 13 files changed, 297 insertions(+), 287 deletions(-) rename app/Events/{DetectedNewIPAddress.php => Security/User/UserLoggedInFromNewIpAddress.php} (82%) rename app/Events/{ActuallyLoggedIn.php => Security/User/UserSuccessfullyLoggedIn.php} (78%) rename app/Events/Test/{OwnerTestNotificationChannel.php => OwnerTestsNotificationChannel.php} (90%) rename app/Events/Test/{UserTestNotificationChannel.php => UserTestsNotificationChannel.php} (97%) create mode 100644 app/Listeners/Security/User/NotifiesUserAboutNewIpAddress.php create mode 100644 app/Listeners/Security/User/StoresNewIpAddress.php create mode 100644 app/Listeners/Test/SendsTestNotification.php diff --git a/app/Events/DetectedNewIPAddress.php b/app/Events/Security/User/UserLoggedInFromNewIpAddress.php similarity index 82% rename from app/Events/DetectedNewIPAddress.php rename to app/Events/Security/User/UserLoggedInFromNewIpAddress.php index 3298a7e164..b73b9f0d3a 100644 --- a/app/Events/DetectedNewIPAddress.php +++ b/app/Events/Security/User/UserLoggedInFromNewIpAddress.php @@ -1,8 +1,7 @@ . */ -declare(strict_types=1); - -namespace FireflyIII\Events; +namespace FireflyIII\Events\Security\User; +use FireflyIII\Events\Event; use FireflyIII\User; use Illuminate\Queue\SerializesModels; -/** - * Class DetectedNewIPAddress - */ -class DetectedNewIPAddress extends Event +class UserLoggedInFromNewIpAddress extends Event { use SerializesModels; diff --git a/app/Events/ActuallyLoggedIn.php b/app/Events/Security/User/UserSuccessfullyLoggedIn.php similarity index 78% rename from app/Events/ActuallyLoggedIn.php rename to app/Events/Security/User/UserSuccessfullyLoggedIn.php index 274af7149a..228fb3423a 100644 --- a/app/Events/ActuallyLoggedIn.php +++ b/app/Events/Security/User/UserSuccessfullyLoggedIn.php @@ -1,8 +1,7 @@ . */ -declare(strict_types=1); - -namespace FireflyIII\Events; +namespace FireflyIII\Events\Security\User; +use FireflyIII\Events\Event; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Queue\SerializesModels; +use InvalidArgumentException; -/** - * Class ActuallyLoggedIn - */ -class ActuallyLoggedIn extends Event +class UserSuccessfullyLoggedIn extends Event { use SerializesModels; @@ -42,5 +38,6 @@ class ActuallyLoggedIn extends Event if ($user instanceof User) { $this->user = $user; } + throw new InvalidArgumentException('User must be an instance of User.'); } } diff --git a/app/Events/Test/OwnerTestNotificationChannel.php b/app/Events/Test/OwnerTestsNotificationChannel.php similarity index 90% rename from app/Events/Test/OwnerTestNotificationChannel.php rename to app/Events/Test/OwnerTestsNotificationChannel.php index 759c0578fc..067583a253 100644 --- a/app/Events/Test/OwnerTestNotificationChannel.php +++ b/app/Events/Test/OwnerTestsNotificationChannel.php @@ -28,7 +28,7 @@ use FireflyIII\Notifications\Notifiables\OwnerNotifiable; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\Log; -class OwnerTestNotificationChannel +class OwnerTestsNotificationChannel { use SerializesModels; @@ -39,7 +39,7 @@ class OwnerTestNotificationChannel */ public function __construct(string $channel, public OwnerNotifiable $owner) { - Log::debug(sprintf('Triggered OwnerTestNotificationChannel("%s")', $channel)); + Log::debug(sprintf('Triggered OwnerTestsNotificationChannels("%s")', $channel)); $this->channel = $channel; } } diff --git a/app/Events/Test/UserTestNotificationChannel.php b/app/Events/Test/UserTestsNotificationChannel.php similarity index 97% rename from app/Events/Test/UserTestNotificationChannel.php rename to app/Events/Test/UserTestsNotificationChannel.php index 6ee93f0190..50e5cea02e 100644 --- a/app/Events/Test/UserTestNotificationChannel.php +++ b/app/Events/Test/UserTestsNotificationChannel.php @@ -28,7 +28,7 @@ use FireflyIII\User; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\Log; -class UserTestNotificationChannel +class UserTestsNotificationChannel { use SerializesModels; diff --git a/app/Handlers/Events/AdminEventHandler.php b/app/Handlers/Events/AdminEventHandler.php index 22f27d7216..9d6e17c7ea 100644 --- a/app/Handlers/Events/AdminEventHandler.php +++ b/app/Handlers/Events/AdminEventHandler.php @@ -26,7 +26,7 @@ namespace FireflyIII\Handlers\Events; use Exception; use FireflyIII\Events\Admin\InvitationCreated; use FireflyIII\Events\NewVersionAvailable; -use FireflyIII\Events\Test\OwnerTestNotificationChannel; +use FireflyIII\Events\Test\OwnerTestsNotificationChannel; use FireflyIII\Notifications\Admin\UserInvitation; use FireflyIII\Notifications\Admin\VersionCheckResult; use FireflyIII\Notifications\Notifiables\OwnerNotifiable; @@ -97,59 +97,4 @@ class AdminEventHandler Log::error($e->getTraceAsString()); } } - - /** - * Sends a test message to an administrator. - */ - public function sendTestNotification(OwnerTestNotificationChannel $event): void - { - Log::debug(sprintf('Now in sendTestNotification("%s")', $event->channel)); - - switch ($event->channel) { - case 'email': - $class = OwnerTestNotificationEmail::class; - - break; - - case 'slack': - $class = OwnerTestNotificationSlack::class; - - break; - - // case 'ntfy': - // $class = OwnerTestNotificationNtfy::class; - // - // break; - - case 'pushover': - $class = OwnerTestNotificationPushover::class; - - break; - - default: - Log::error(sprintf('Unknown channel "%s" in sendTestNotification method.', $event->channel)); - - return; - } - Log::debug(sprintf('Will send %s as a notification.', $class)); - - try { - Notification::send($event->owner, new $class()); - } catch (Exception $e) { - $message = $e->getMessage(); - if (str_contains($message, 'Bcc')) { - Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); - - return; - } - if (str_contains($message, 'RFC 2822')) { - Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); - - return; - } - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); - } - Log::debug(sprintf('If you see no errors above this line, test notification was sent over channel "%s"', $event->channel)); - } } diff --git a/app/Handlers/Events/UserEventHandler.php b/app/Handlers/Events/UserEventHandler.php index 06a532196f..4a54563caf 100644 --- a/app/Handlers/Events/UserEventHandler.php +++ b/app/Handlers/Events/UserEventHandler.php @@ -23,16 +23,12 @@ declare(strict_types=1); namespace FireflyIII\Handlers\Events; -use Carbon\Carbon; use Database\Seeders\ExchangeRateSeeder; use Exception; use FireflyIII\Enums\UserRoleEnum; -use FireflyIII\Events\ActuallyLoggedIn; use FireflyIII\Events\Admin\InvitationCreated; -use FireflyIII\Events\DetectedNewIPAddress; use FireflyIII\Events\RegisteredUser; use FireflyIII\Events\RequestedNewPassword; -use FireflyIII\Events\Test\UserTestNotificationChannel; use FireflyIII\Events\UserChangedEmail; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Mail\ConfirmEmailChangeMail; @@ -42,10 +38,6 @@ use FireflyIII\Models\GroupMembership; use FireflyIII\Models\UserGroup; use FireflyIII\Models\UserRole; use FireflyIII\Notifications\Admin\UserRegistration as AdminRegistrationNotification; -use FireflyIII\Notifications\Test\UserTestNotificationEmail; -use FireflyIII\Notifications\Test\UserTestNotificationPushover; -use FireflyIII\Notifications\Test\UserTestNotificationSlack; -use FireflyIII\Notifications\User\UserLogin; use FireflyIII\Notifications\User\UserNewPassword; use FireflyIII\Notifications\User\UserRegistration as UserRegistrationNotification; use FireflyIII\Repositories\User\UserRepositoryInterface; @@ -90,8 +82,8 @@ class UserEventHandler $repository = app(UserRepositoryInterface::class); /** @var User $user */ - $user = $event->user; - $count = $repository->count(); + $user = $event->user; + $count = $repository->count(); // only act when there is 1 user in the system and he has no admin rights. if (1 === $count && !$repository->hasRole($user, 'owner')) { @@ -123,13 +115,13 @@ class UserEventHandler */ public function createGroupMembership(RegisteredUser $event): void { - $user = $event->user; - $groupExists = true; - $groupTitle = $user->email; - $index = 1; + $user = $event->user; + $groupExists = true; + $groupTitle = $user->email; + $index = 1; /** @var null|UserGroup $group */ - $group = null; + $group = null; // create a new group. while ($groupExists) { // @phpstan-ignore-line @@ -139,7 +131,7 @@ class UserEventHandler break; } - $groupTitle = sprintf('%s-%d', $user->email, $index); + $groupTitle = sprintf('%s-%d', $user->email, $index); ++$index; if ($index > 99) { throw new FireflyException('Email address can no longer be used for registrations.'); @@ -147,7 +139,7 @@ class UserEventHandler } /** @var null|UserRole $role */ - $role = UserRole::where('title', UserRoleEnum::OWNER->value)->first(); + $role = UserRole::where('title', UserRoleEnum::OWNER->value)->first(); if (null === $role) { throw new FireflyException('The user role is unexpectedly empty. Did you run all migrations?'); } @@ -171,7 +163,7 @@ class UserEventHandler $repository = app(UserRepositoryInterface::class); /** @var User $user */ - $user = $event->user; + $user = $event->user; if ($repository->hasRole($user, 'demo')) { // set user back to English. Preferences::setForUser($user, 'language', 'en_US'); @@ -181,46 +173,6 @@ class UserEventHandler } } - public function notifyNewIPAddress(DetectedNewIPAddress $event): void - { - $user = $event->user; - - if ($user->hasRole('demo')) { - return; // do not email demo user. - } - - $list = Preferences::getForUser($user, 'login_ip_history', [])->data; - if (!is_array($list)) { - $list = []; - } - - /** @var array $entry */ - foreach ($list as $index => $entry) { - if (false === $entry['notified']) { - try { - Notification::send($user, new UserLogin()); - } catch (Exception $e) { - $message = $e->getMessage(); - if (str_contains($message, 'Bcc')) { - Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); - - return; - } - if (str_contains($message, 'RFC 2822')) { - Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); - - return; - } - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); - } - } - $list[$index]['notified'] = true; - } - - Preferences::setForUser($user, 'login_ip_history', $list); - } - public function sendAdminRegistrationNotification(RegisteredUser $event): void { $sendMail = (bool)FireflyConfig::get('notification_admin_new_reg', true)->data; @@ -366,116 +318,4 @@ class UserEventHandler } } } - - /** - * Sends a test message to an administrator. - */ - public function sendTestNotification(UserTestNotificationChannel $event): void - { - Log::debug(sprintf('Now in (user) sendTestNotification("%s")', $event->channel)); - - switch ($event->channel) { - case 'email': - $class = UserTestNotificationEmail::class; - - break; - - case 'slack': - $class = UserTestNotificationSlack::class; - - break; - - // case 'ntfy': - // $class = UserTestNotificationNtfy::class; - // - // break; - - case 'pushover': - $class = UserTestNotificationPushover::class; - - break; - - default: - Log::error(sprintf('Unknown channel "%s" in (user) sendTestNotification method.', $event->channel)); - - return; - } - Log::debug(sprintf('Will send %s as a notification.', $class)); - - try { - Notification::send($event->user, new $class()); - } catch (Exception $e) { - $message = $e->getMessage(); - if (str_contains($message, 'Bcc')) { - Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); - - return; - } - if (str_contains($message, 'RFC 2822')) { - Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); - - return; - } - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); - } - Log::debug(sprintf('If you see no errors above this line, test notification was sent over channel "%s"', $event->channel)); - } - - public function storeUserIPAddress(ActuallyLoggedIn $event): void - { - Log::debug('Now in storeUserIPAddress'); - $user = $event->user; - - if ($user->hasRole('demo')) { - Log::debug('Do not log demo user logins'); - - return; - } - - try { - /** @var array $preference */ - $preference = Preferences::getForUser($user, 'login_ip_history', [])->data; - } catch (FireflyException $e) { - // don't care. - Log::error($e->getMessage()); - - return; - } - $inArray = false; - $ip = request()->ip(); - Log::debug(sprintf('User logging in from IP address %s', $ip)); - - // update array if in array - foreach ($preference as $index => $row) { - if ($row['ip'] === $ip) { - Log::debug('Found IP in array, refresh time.'); - $preference[$index]['time'] = now(config('app.timezone'))->format('Y-m-d H:i:s'); - $inArray = true; - } - // clean up old entries (6 months) - $carbon = Carbon::createFromFormat('Y-m-d H:i:s', $preference[$index]['time']); - if ($carbon instanceof Carbon && $carbon->diffInMonths(today(), true) > 6) { - Log::debug(sprintf('Entry for %s is very old, remove it.', $row['ip'])); - unset($preference[$index]); - } - } - // add to array if not the case: - if (false === $inArray) { - $preference[] = [ - 'ip' => $ip, - 'time' => now(config('app.timezone'))->format('Y-m-d H:i:s'), - 'notified' => false, - ]; - } - $preference = array_values($preference); - - /** @var bool $send */ - $send = Preferences::getForUser($user, 'notification_user_login', true)->data; - Preferences::setForUser($user, 'login_ip_history', $preference); - - if (false === $inArray && true === $send) { - event(new DetectedNewIPAddress($user)); - } - } } diff --git a/app/Http/Controllers/Admin/NotificationController.php b/app/Http/Controllers/Admin/NotificationController.php index f2e268cf87..879bdfb0d6 100644 --- a/app/Http/Controllers/Admin/NotificationController.php +++ b/app/Http/Controllers/Admin/NotificationController.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Admin; -use FireflyIII\Events\Test\OwnerTestNotificationChannel; +use FireflyIII\Events\Test\OwnerTestsNotificationChannel; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Requests\NotificationRequest; use FireflyIII\Notifications\Notifiables\OwnerNotifiable; @@ -127,7 +127,7 @@ class NotificationController extends Controller case 'ntfy': $owner = new OwnerNotifiable(); Log::debug(sprintf('Now in testNotification("%s") controller.', $channel)); - event(new OwnerTestNotificationChannel($channel, $owner)); + event(new OwnerTestsNotificationChannel($channel, $owner)); session()->flash('success', (string) trans('firefly.notification_test_executed', ['channel' => $channel])); } diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 8380f87c39..2c7121c2e7 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -24,9 +24,9 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Auth; use Carbon\Carbon; -use FireflyIII\Events\ActuallyLoggedIn; use FireflyIII\Events\Security\System\UnknownUserTriedLogin; use FireflyIII\Events\Security\User\UserFailedLoginAttempt; +use FireflyIII\Events\Security\User\UserSuccessfullyLoggedIn; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Providers\RouteServiceProvider; @@ -70,7 +70,7 @@ class LoginController extends Controller protected string $redirectTo = RouteServiceProvider::HOME; private UserRepositoryInterface $repository; - private string $username = 'email'; + private string $username = 'email'; /** * Create a new controller instance. @@ -87,7 +87,7 @@ class LoginController extends Controller * * @throws ValidationException */ - public function login(Request $request): JsonResponse|RedirectResponse + public function login(Request $request): JsonResponse | RedirectResponse { $username = $request->get($this->username()); Log::channel('audit')->info(sprintf('User is trying to login using "%s"', $username)); @@ -105,8 +105,7 @@ class LoginController extends Controller $this->username => trans('auth.failed'), ] ) - ->onlyInput($this->username) - ; + ->onlyInput($this->username); } Log::debug('Login data is present.'); @@ -129,7 +128,7 @@ class LoginController extends Controller // send a custom login event because laravel will also fire a login event if a "remember me"-cookie // restores the event. - event(new ActuallyLoggedIn($this->guard()->user())); + event(new UserSuccessfullyLoggedIn($this->guard()->user())); return $this->sendLoginResponse($request); } @@ -187,10 +186,10 @@ class LoginController extends Controller /** * Log the user out of the application. */ - public function logout(Request $request): Redirector|RedirectResponse|Response + public function logout(Request $request): Redirector | RedirectResponse | Response { - $authGuard = config('firefly.authentication_guard'); - $logoutUrl = config('firefly.custom_logout_url'); + $authGuard = config('firefly.authentication_guard'); + $logoutUrl = config('firefly.custom_logout_url'); if ('remote_user_guard' === $authGuard && '' !== $logoutUrl) { return redirect($logoutUrl); } @@ -224,13 +223,13 @@ class LoginController extends Controller * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function showLoginForm(Request $request): Factory|Redirector|RedirectResponse|View + public function showLoginForm(Request $request): Factory | Redirector | RedirectResponse | View { Log::channel('audit')->info('Show login form (1.1).'); - $count = DB::table('users')->count(); - $guard = config('auth.defaults.guard'); - $title = (string)trans('firefly.login_page_title'); + $count = DB::table('users')->count(); + $guard = config('auth.defaults.guard'); + $title = (string)trans('firefly.login_page_title'); if (0 === $count && 'web' === $guard) { return redirect(route('register')); @@ -250,15 +249,15 @@ class LoginController extends Controller $allowReset = false; } - $email = $request->old('email'); - $remember = $request->old('remember'); + $email = $request->old('email'); + $remember = $request->old('remember'); - $storeInCookie = config('google2fa.store_in_cookie', false); + $storeInCookie = config('google2fa.store_in_cookie', false); if (false !== $storeInCookie) { $cookieName = config('google2fa.cookie_name', 'google2fa_token'); - Cookie::queue(Cookie::make($cookieName, 'invalid-'.Carbon::now()->getTimestamp())); + Cookie::queue(Cookie::make($cookieName, 'invalid-' . Carbon::now()->getTimestamp())); } - $usernameField = $this->username(); + $usernameField = $this->username(); return view('auth.login', ['allowRegistration' => $allowRegistration, 'email' => $email, 'remember' => $remember, 'allowReset' => $allowReset, 'title' => $title, 'usernameField' => $usernameField]); } diff --git a/app/Http/Controllers/PreferencesController.php b/app/Http/Controllers/PreferencesController.php index 415e27e622..f0460a2ae0 100644 --- a/app/Http/Controllers/PreferencesController.php +++ b/app/Http/Controllers/PreferencesController.php @@ -26,7 +26,7 @@ namespace FireflyIII\Http\Controllers; use Carbon\Carbon; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Events\Preferences\UserGroupChangedPrimaryCurrency; -use FireflyIII\Events\Test\UserTestNotificationChannel; +use FireflyIII\Events\Test\UserTestsNotificationChannel; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Requests\PreferencesRequest; use FireflyIII\Models\Account; @@ -348,7 +348,7 @@ class PreferencesController extends Controller /** @var User $user */ $user = auth()->user(); Log::debug(sprintf('Now in testNotification("%s") controller.', $channel)); - event(new UserTestNotificationChannel($channel, $user)); + event(new UserTestsNotificationChannel($channel, $user)); session()->flash('success', (string)trans('firefly.notification_test_executed', ['channel' => $channel])); } diff --git a/app/Listeners/Security/User/NotifiesUserAboutNewIpAddress.php b/app/Listeners/Security/User/NotifiesUserAboutNewIpAddress.php new file mode 100644 index 0000000000..06609cc1d4 --- /dev/null +++ b/app/Listeners/Security/User/NotifiesUserAboutNewIpAddress.php @@ -0,0 +1,71 @@ +. + */ + +namespace FireflyIII\Listeners\Security\User; + +use Exception; +use FireflyIII\Events\Security\User\UserLoggedInFromNewIpAddress; +use FireflyIII\Notifications\User\UserLogin; +use FireflyIII\Support\Facades\Preferences; +use Illuminate\Support\Facades\Log; +use Illuminate\Support\Facades\Notification; + +class NotifiesUserAboutNewIpAddress +{ +public function handle(UserLoggedInFromNewIpAddress $event): void { + $user = $event->user; + + if ($user->hasRole('demo')) { + return; // do not email demo user. + } + + $list = Preferences::getForUser($user, 'login_ip_history', [])->data; + if (!is_array($list)) { + $list = []; + } + + /** @var array $entry */ + foreach ($list as $index => $entry) { + if (false === $entry['notified']) { + try { + Notification::send($user, new UserLogin()); + } catch (Exception $e) { + $message = $e->getMessage(); + if (str_contains($message, 'Bcc')) { + Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + + return; + } + if (str_contains($message, 'RFC 2822')) { + Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + + return; + } + Log::error($e->getMessage()); + Log::error($e->getTraceAsString()); + } + } + $list[$index]['notified'] = true; + } + + Preferences::setForUser($user, 'login_ip_history', $list); +} +} diff --git a/app/Listeners/Security/User/StoresNewIpAddress.php b/app/Listeners/Security/User/StoresNewIpAddress.php new file mode 100644 index 0000000000..70daf2aeb3 --- /dev/null +++ b/app/Listeners/Security/User/StoresNewIpAddress.php @@ -0,0 +1,81 @@ +. + */ + +namespace FireflyIII\Listeners\Security\User; + +use Carbon\Carbon; +use FireflyIII\Events\Security\User\UserLoggedInFromNewIpAddress; +use FireflyIII\Events\Security\User\UserSuccessfullyLoggedIn; +use FireflyIII\Support\Facades\Preferences; +use Illuminate\Support\Facades\Log; + +class StoresNewIpAddress +{ + public function handle(UserSuccessfullyLoggedIn $event): void + { + Log::debug('Now in storeUserIPAddress'); + $user = $event->user; + + if ($user->hasRole('demo')) { + Log::debug('Do not log demo user logins'); + + return; + } + /** @var array $preference */ + $preference = Preferences::getForUser($user, 'login_ip_history', [])->data; + $inArray = false; + $ip = request()->ip(); + Log::debug(sprintf('User logging in from IP address %s', $ip)); + + // update array if in array + foreach ($preference as $index => $row) { + if ($row['ip'] === $ip) { + Log::debug('Found IP in array, refresh time.'); + $preference[$index]['time'] = now(config('app.timezone'))->format('Y-m-d H:i:s'); + $inArray = true; + } + // clean up old entries (6 months) + $carbon = Carbon::createFromFormat('Y-m-d H:i:s', $preference[$index]['time']); + if ($carbon instanceof Carbon && $carbon->diffInMonths(today(), true) > 6) { + Log::debug(sprintf('Entry for %s is very old, remove it.', $row['ip'])); + unset($preference[$index]); + } + } + // add to array if not the case: + if (false === $inArray) { + $preference[] = [ + 'ip' => $ip, + 'time' => now(config('app.timezone'))->format('Y-m-d H:i:s'), + 'notified' => false, + ]; + } + $preference = array_values($preference); + + /** @var bool $send */ + $send = Preferences::getForUser($user, 'notification_user_login', true)->data; + Preferences::setForUser($user, 'login_ip_history', $preference); + + if (false === $inArray && true === $send) { + event(new UserLoggedInFromNewIpAddress($user)); + } + } + +} diff --git a/app/Listeners/Test/SendsTestNotification.php b/app/Listeners/Test/SendsTestNotification.php new file mode 100644 index 0000000000..44459edb37 --- /dev/null +++ b/app/Listeners/Test/SendsTestNotification.php @@ -0,0 +1,89 @@ +. + */ + +namespace FireflyIII\Listeners\Test; + +use Exception; +use FireflyIII\Events\Test\OwnerTestsNotificationChannel; +use FireflyIII\Events\Test\UserTestsNotificationChannel; +use FireflyIII\Notifications\Test\OwnerTestNotificationEmail; +use FireflyIII\Notifications\Test\OwnerTestNotificationPushover; +use FireflyIII\Notifications\Test\OwnerTestNotificationSlack; +use FireflyIII\Notifications\Test\UserTestNotificationEmail; +use FireflyIII\Notifications\Test\UserTestNotificationPushover; +use FireflyIII\Notifications\Test\UserTestNotificationSlack; +use Illuminate\Support\Facades\Log; +use Illuminate\Support\Facades\Notification; + +class SendsTestNotification +{ + public function handle(OwnerTestsNotificationChannel | UserTestsNotificationChannel $event): void + { + Log::debug(sprintf('Now in SendsTestNotification::handle(%s->"%s")', get_class($event), $event->channel)); + + $type = str_contains(get_class($event), 'Owner') ? 'owner' : 'user'; + $key = sprintf('%s-%s', $type, $event->channel); + switch ($key) { + case 'user-email': + $class = UserTestNotificationEmail::class; + break; + case 'user-slack': + $class = UserTestNotificationSlack::class; + break; + case 'user-pushover': + $class = UserTestNotificationPushover::class; + break; + case 'owner-email': + $class = OwnerTestNotificationEmail::class; + break; + case 'owner-slack': + $class = OwnerTestNotificationSlack::class; + break; + case 'owner-pushover': + $class = OwnerTestNotificationPushover::class; + break; + default: + Log::error(sprintf('Unknown key "%s" in sendTestNotification method.', $key)); + + return; + } + Log::debug(sprintf('Will send %s as a notification.', $class)); + + try { + Notification::send($event->user, new $class()); + } catch (Exception $e) { + $message = $e->getMessage(); + if (str_contains($message, 'Bcc')) { + Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + + return; + } + if (str_contains($message, 'RFC 2822')) { + Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + + return; + } + Log::error($e->getMessage()); + Log::error($e->getTraceAsString()); + } + Log::debug(sprintf('If you see no errors above this line, test notification was sent over channel "%s"', $event->channel)); + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index d859579f45..f76cd874d0 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -23,10 +23,8 @@ declare(strict_types=1); namespace FireflyIII\Providers; -use FireflyIII\Events\ActuallyLoggedIn; use FireflyIII\Events\Admin\InvitationCreated; use FireflyIII\Events\DestroyedTransactionGroup; -use FireflyIII\Events\DetectedNewIPAddress; use FireflyIII\Events\Model\TransactionGroup\TriggeredStoredTransactionGroup; use FireflyIII\Events\NewVersionAvailable; use FireflyIII\Events\Preferences\UserGroupChangedPrimaryCurrency; @@ -37,8 +35,6 @@ use FireflyIII\Events\RequestedSendWebhookMessages; use FireflyIII\Events\RequestedVersionCheckStatus; use FireflyIII\Events\StoredAccount; use FireflyIII\Events\StoredTransactionGroup; -use FireflyIII\Events\Test\OwnerTestNotificationChannel; -use FireflyIII\Events\Test\UserTestNotificationChannel; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Events\UpdatedAccount; use FireflyIII\Events\UpdatedTransactionGroup; @@ -73,12 +69,9 @@ class EventServiceProvider extends ServiceProvider 'FireflyIII\Handlers\Events\UserEventHandler@checkSingleUserIsAdmin', 'FireflyIII\Handlers\Events\UserEventHandler@demoUserBackToEnglish', ], - ActuallyLoggedIn::class => [ - 'FireflyIII\Handlers\Events\UserEventHandler@storeUserIPAddress', - ], - DetectedNewIPAddress::class => [ - 'FireflyIII\Handlers\Events\UserEventHandler@notifyNewIPAddress', - ], + // DetectedNewIPAddress::class => [ + // 'FireflyIII\Handlers\Events\UserEventHandler@notifyNewIPAddress', + // ], RequestedVersionCheckStatus::class => [ 'FireflyIII\Handlers\Events\VersionCheckEventHandler@checkForUpdates', ], @@ -90,18 +83,18 @@ class EventServiceProvider extends ServiceProvider RequestedNewPassword::class => [ 'FireflyIII\Handlers\Events\UserEventHandler@sendNewPassword', ], - UserTestNotificationChannel::class => [ - 'FireflyIII\Handlers\Events\UserEventHandler@sendTestNotification', - ], + // UserTestsNotificationChannel::class => [ + // 'FireflyIII\Handlers\Events\UserEventHandler@sendTestNotification', + // ], // is a User related event. UserChangedEmail::class => [ 'FireflyIII\Handlers\Events\UserEventHandler@sendEmailChangeConfirmMail', 'FireflyIII\Handlers\Events\UserEventHandler@sendEmailChangeUndoMail', ], // admin related - OwnerTestNotificationChannel::class => [ - 'FireflyIII\Handlers\Events\AdminEventHandler@sendTestNotification', - ], + // OwnerTestsNotificationChannel::class => [ + // 'FireflyIII\Handlers\Events\AdminEventHandler@sendTestNotification', + // ], NewVersionAvailable::class => [ 'FireflyIII\Handlers\Events\AdminEventHandler@sendNewVersion', ],