mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
Unknown user warning.
This commit is contained in:
parent
d995bfc081
commit
0e5eb036b0
39
app/Events/Security/UnknownUserAttemptedLogin.php
Normal file
39
app/Events/Security/UnknownUserAttemptedLogin.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/*
|
||||
* UnknownUserAttemptedLogin.php
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Events\Security;
|
||||
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class UnknownUserAttemptedLogin
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
public string $address;
|
||||
|
||||
public function __construct(string $address)
|
||||
{
|
||||
$this->address = $address;
|
||||
}
|
||||
}
|
@ -25,7 +25,9 @@ namespace FireflyIII\Handlers\Events;
|
||||
|
||||
use FireflyIII\Events\Admin\InvitationCreated;
|
||||
use FireflyIII\Events\NewVersionAvailable;
|
||||
use FireflyIII\Events\Security\UnknownUserAttemptedLogin;
|
||||
use FireflyIII\Events\Test\TestNotificationChannel;
|
||||
use FireflyIII\Notifications\Admin\UnknownUserLoginAttempt;
|
||||
use FireflyIII\Notifications\Admin\UserInvitation;
|
||||
use FireflyIII\Notifications\Admin\VersionCheckResult;
|
||||
use FireflyIII\Notifications\Notifiables\OwnerNotifiable;
|
||||
@ -41,6 +43,28 @@ use Illuminate\Support\Facades\Notification;
|
||||
*/
|
||||
class AdminEventHandler
|
||||
{
|
||||
public function sendLoginAttemptNotification(UnknownUserAttemptedLogin $event): void {
|
||||
try {
|
||||
$owner = new OwnerNotifiable();
|
||||
Notification::send($owner, new UnknownUserLoginAttempt($event->address));
|
||||
} catch (\Exception $e) { // @phpstan-ignore-line
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
app('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')) {
|
||||
app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
app('log')->error($e->getMessage());
|
||||
app('log')->error($e->getTraceAsString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function sendInvitationNotification(InvitationCreated $event): void
|
||||
{
|
||||
$sendMail = app('fireflyconfig')->get('notification_invite_created', true)->data;
|
||||
|
@ -25,9 +25,12 @@ namespace FireflyIII\Http\Controllers\Auth;
|
||||
|
||||
use Cookie;
|
||||
use FireflyIII\Events\ActuallyLoggedIn;
|
||||
use FireflyIII\Events\Security\UnknownUserAttemptedLogin;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Notifications\Notifiables\OwnerNotifiable;
|
||||
use FireflyIII\Providers\RouteServiceProvider;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Contracts\View\View;
|
||||
@ -57,6 +60,7 @@ class LoginController extends Controller
|
||||
* Where to redirect users after login.
|
||||
*/
|
||||
protected string $redirectTo = RouteServiceProvider::HOME;
|
||||
private UserRepositoryInterface $repository;
|
||||
|
||||
private string $username;
|
||||
|
||||
@ -68,6 +72,7 @@ class LoginController extends Controller
|
||||
parent::__construct();
|
||||
$this->username = 'email';
|
||||
$this->middleware('guest')->except('logout');
|
||||
$this->repository = app(UserRepositoryInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -122,6 +127,11 @@ class LoginController extends Controller
|
||||
return $this->sendLoginResponse($request);
|
||||
}
|
||||
app('log')->warning('Login attempt failed.');
|
||||
$username = (string) $request->get($this->username());
|
||||
if(null === $this->repository->findByEmail($username)) {
|
||||
// send event to owner.
|
||||
event(new UnknownUserAttemptedLogin($username));
|
||||
}
|
||||
|
||||
// Copied directly from AuthenticatesUsers, but with logging added:
|
||||
// If the login attempt was unsuccessful we will increment the number of attempts
|
||||
|
@ -25,7 +25,6 @@ namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Carbon\Exceptions\InvalidFormatException;
|
||||
use FireflyIII\Events\NewVersionAvailable;
|
||||
use FireflyIII\Events\RequestedVersionCheckStatus;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
@ -63,8 +62,8 @@ class HomeController extends Controller
|
||||
*/
|
||||
public function dateRange(Request $request): JsonResponse
|
||||
{
|
||||
$stringStart = '';
|
||||
$stringEnd = '';
|
||||
$stringStart = '';
|
||||
$stringEnd = '';
|
||||
|
||||
try {
|
||||
$stringStart = e((string) $request->get('start'));
|
||||
@ -99,7 +98,7 @@ class HomeController extends Controller
|
||||
app('log')->debug('Range is now marked as "custom".');
|
||||
}
|
||||
|
||||
$diff = $start->diffInDays($end, true) + 1;
|
||||
$diff = $start->diffInDays($end, true) + 1;
|
||||
|
||||
if ($diff > 366) {
|
||||
$request->session()->flash('warning', (string) trans('firefly.warning_much_data', ['days' => (int) $diff]));
|
||||
@ -154,13 +153,13 @@ class HomeController extends Controller
|
||||
}
|
||||
|
||||
/** @var Carbon $start */
|
||||
$start = session('start', today(config('app.timezone'))->startOfMonth());
|
||||
$start = session('start', today(config('app.timezone'))->startOfMonth());
|
||||
|
||||
/** @var Carbon $end */
|
||||
$end = session('end', today(config('app.timezone'))->endOfMonth());
|
||||
$accounts = $repository->getAccountsById($frontpageArray);
|
||||
$today = today(config('app.timezone'));
|
||||
$accounts = $accounts->sortBy('order'); // sort frontpage accounts by order
|
||||
$end = session('end', today(config('app.timezone'))->endOfMonth());
|
||||
$accounts = $repository->getAccountsById($frontpageArray);
|
||||
$today = today(config('app.timezone'));
|
||||
$accounts = $accounts->sortBy('order'); // sort frontpage accounts by order
|
||||
|
||||
app('log')->debug('Frontpage accounts are ', $frontpageArray);
|
||||
|
||||
@ -170,14 +169,14 @@ class HomeController extends Controller
|
||||
// collect groups for each transaction.
|
||||
foreach ($accounts as $account) {
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector->setAccounts(new Collection([$account]))->withAccountInformation()->setRange($start, $end)->setLimit(10)->setPage(1);
|
||||
$set = $collector->getExtractedJournals();
|
||||
$transactions[] = ['transactions' => $set, 'account' => $account];
|
||||
}
|
||||
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
$user = auth()->user();
|
||||
event(new RequestedVersionCheckStatus($user));
|
||||
|
||||
return view('index', compact('count', 'subTitle', 'transactions', 'billCount', 'start', 'end', 'today', 'pageTitle'));
|
||||
@ -188,11 +187,11 @@ class HomeController extends Controller
|
||||
$subTitle = (string) trans('firefly.welcome_back');
|
||||
$pageTitle = (string) trans('firefly.main_dashboard_page_title');
|
||||
|
||||
$start = session('start', today(config('app.timezone'))->startOfMonth());
|
||||
$end = session('end', today(config('app.timezone'))->endOfMonth());
|
||||
$start = session('start', today(config('app.timezone'))->startOfMonth());
|
||||
$end = session('end', today(config('app.timezone'))->endOfMonth());
|
||||
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
$user = auth()->user();
|
||||
event(new RequestedVersionCheckStatus($user));
|
||||
|
||||
return view('index', compact('subTitle', 'start', 'end', 'pageTitle'));
|
||||
|
@ -111,9 +111,10 @@ class PreferencesController extends Controller
|
||||
|
||||
// notification preferences (single value for each):
|
||||
$notifications = [];
|
||||
die('fix the reference to the available notifications.');
|
||||
foreach (config('firefly.available_notifications') as $notification) {
|
||||
$notifications[$notification] = app('preferences')->get(sprintf('notification_%s', $notification), true)->data;
|
||||
foreach (config('notifications.notifications.user') as $key => $info) {
|
||||
if($info['enabled']) {
|
||||
$notifications[$key] = app('preferences')->get(sprintf('notification_%s', $key), true)->data;
|
||||
}
|
||||
}
|
||||
|
||||
ksort($languages);
|
||||
|
128
app/Notifications/Admin/UnknownUserLoginAttempt.php
Normal file
128
app/Notifications/Admin/UnknownUserLoginAttempt.php
Normal file
@ -0,0 +1,128 @@
|
||||
<?php
|
||||
/*
|
||||
* UnknownUserLoginAttempt.php
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Notifications\Admin;
|
||||
|
||||
use FireflyIII\Notifications\Notifiables\OwnerNotifiable;
|
||||
use FireflyIII\Notifications\ReturnsAvailableChannels;
|
||||
use FireflyIII\Notifications\ReturnsSettings;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Messages\SlackMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
use NotificationChannels\Pushover\PushoverMessage;
|
||||
use Ntfy\Message;
|
||||
|
||||
class UnknownUserLoginAttempt extends Notification
|
||||
{
|
||||
use Queueable;
|
||||
private string $address;
|
||||
|
||||
/**
|
||||
* Create a new notification instance.
|
||||
*/
|
||||
public function __construct(string $address)
|
||||
{
|
||||
$this->address = $address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array representation of the notification.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function toArray(OwnerNotifiable $notifiable)
|
||||
{
|
||||
return [
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mail representation of the notification.
|
||||
*
|
||||
* @return MailMessage
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function toMail(OwnerNotifiable $notifiable): MailMessage
|
||||
{
|
||||
return new MailMessage()
|
||||
->markdown('emails.owner.unknown-user', ['address' => $this->address])
|
||||
->subject((string) trans('email.unknown_user_subject'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Slack representation of the notification.
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function toSlack(OwnerNotifiable $notifiable): SlackMessage
|
||||
{
|
||||
return new SlackMessage()->content(
|
||||
(string) trans('email.unknown_user_body', ['address' => $this->address])
|
||||
);
|
||||
}
|
||||
|
||||
public function toPushover(OwnerNotifiable $notifiable): PushoverMessage
|
||||
{
|
||||
return PushoverMessage::create((string) trans('email.unknown_user_message', ['address' => $this->address]))
|
||||
->title((string) trans('email.unknown_user_subject'));
|
||||
}
|
||||
|
||||
public function toNtfy(OwnerNotifiable $notifiable): Message
|
||||
{
|
||||
$settings = ReturnsSettings::getSettings('ntfy', 'owner', null);
|
||||
|
||||
// overrule config.
|
||||
config(['ntfy-notification-channel.server' => $settings['ntfy_server']]);
|
||||
config(['ntfy-notification-channel.topic' => $settings['ntfy_topic']]);
|
||||
|
||||
if ($settings['ntfy_auth']) {
|
||||
// overrule auth as well.
|
||||
config(['ntfy-notification-channel.authentication.enabled' => true]);
|
||||
config(['ntfy-notification-channel.authentication.username' => $settings['ntfy_user']]);
|
||||
config(['ntfy-notification-channel.authentication.password' => $settings['ntfy_pass']]);
|
||||
}
|
||||
|
||||
$message = new Message();
|
||||
$message->topic($settings['ntfy_topic']);
|
||||
$message->title((string) trans('email.unknown_user_subject'));
|
||||
$message->body((string) trans('email.unknown_user_message', ['address' => $this->address]));
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notification's delivery channels.
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function via(OwnerNotifiable $notifiable)
|
||||
{
|
||||
return ReturnsAvailableChannels::returnChannels('owner');
|
||||
}
|
||||
}
|
@ -47,6 +47,7 @@ use FireflyIII\Events\Security\MFABackupNoLeft;
|
||||
use FireflyIII\Events\Security\MFAManyFailedAttempts;
|
||||
use FireflyIII\Events\Security\MFANewBackupCodes;
|
||||
use FireflyIII\Events\Security\MFAUsedBackupCode;
|
||||
use FireflyIII\Events\Security\UnknownUserAttemptedLogin;
|
||||
use FireflyIII\Events\StoredAccount;
|
||||
use FireflyIII\Events\StoredTransactionGroup;
|
||||
use FireflyIII\Events\Test\TestNotificationChannel;
|
||||
@ -146,6 +147,9 @@ class EventServiceProvider extends ServiceProvider
|
||||
'FireflyIII\Handlers\Events\AdminEventHandler@sendInvitationNotification',
|
||||
'FireflyIII\Handlers\Events\UserEventHandler@sendRegistrationInvite',
|
||||
],
|
||||
UnknownUserAttemptedLogin::class => [
|
||||
'FireflyIII\Handlers\Events\AdminEventHandler@sendLoginAttemptNotification',
|
||||
],
|
||||
|
||||
// is a Transaction Journal related event.
|
||||
StoredTransactionGroup::class => [
|
||||
|
@ -31,11 +31,18 @@ return [
|
||||
],
|
||||
'notifications' => [
|
||||
'user' => [
|
||||
'some_notification' => [
|
||||
'enabled' => true,
|
||||
'email' => '',
|
||||
'slack' => '',
|
||||
],
|
||||
'bill_reminder' => ['enabled' => true, 'configurable' => true],
|
||||
'new_access_token' => ['enabled' => true, 'configurable' => true],
|
||||
'transaction_creation' => ['enabled' => true, 'configurable' => true],
|
||||
'user_login' => ['enabled' => true, 'configurable' => true],
|
||||
'rule_action_failures' => ['enabled' => true, 'configurable' => true],
|
||||
'new_password' => ['enabled' => true, 'configurable' => false],
|
||||
'enabled_mfa' => ['enabled' => true, 'configurable' => false],
|
||||
'disabled_mfa' => ['enabled' => true, 'configurable' => false],
|
||||
'few_left_mfa' => ['enabled' => true, 'configurable' => false],
|
||||
'no_left_mfa' => ['enabled' => true, 'configurable' => false],
|
||||
'many_failed_mfa' => ['enabled' => true, 'configurable' => false],
|
||||
'new_backup_codes' => ['enabled' => true, 'configurable' => false],
|
||||
],
|
||||
'owner' => [
|
||||
//'invitation_created' => ['enabled' => true],
|
||||
@ -45,6 +52,7 @@ return [
|
||||
'new_version' => ['enabled' => true],
|
||||
'invite_created' => ['enabled' => true],
|
||||
'invite_redeemed' => ['enabled' => true],
|
||||
'unknown_user_attempt' => ['enabled' => true],
|
||||
],
|
||||
],
|
||||
// // notifications
|
||||
|
@ -61,6 +61,11 @@ return [
|
||||
'access_token_created_explanation' => 'With this token, they can access **all** of your financial records through the Firefly III API.',
|
||||
'access_token_created_revoke' => 'If this wasn\'t you, please revoke this token as soon as possible at :url',
|
||||
|
||||
// unknown user login attempt
|
||||
'unknown_user_subject' => 'An unknown user tried to log in',
|
||||
'unknown_user_body' => 'An unknown user tried to log in to Firefly III. The email address they used was ":address".',
|
||||
'unknown_user_message' => 'The email address they used was ":address".',
|
||||
|
||||
// registered
|
||||
'registered_subject' => 'Welcome to Firefly III!',
|
||||
'registered_subject_admin' => 'A new user has registered',
|
||||
|
@ -1376,7 +1376,14 @@ return [
|
||||
'pref_notification_new_access_token' => 'Alert when a new API access token is created',
|
||||
'pref_notification_transaction_creation' => 'Alert when a transaction is created automatically',
|
||||
'pref_notification_user_login' => 'Alert when you login from a new location',
|
||||
'pref_notification_rule_action_failures' => 'Alert when rule actions fail to execute (Slack or Discord only)',
|
||||
'pref_notification_rule_action_failures' => 'Alert when rule actions fail to execute (not over email)',
|
||||
'pref_notification_new_password' => 'Your password changed',
|
||||
'pref_notification_enabled_mfa' => 'Multi factor authentication is enabled',
|
||||
'pref_notification_disabled_mfa' => 'Multi factor authentication is disabled',
|
||||
'pref_notification_few_left_mfa' => 'You have just a few backup codes left',
|
||||
'pref_notification_no_left_mfa' => 'You have no backup codes left',
|
||||
'pref_notification_many_failed_mfa' => 'The multi factor authentication check keeps failing',
|
||||
'pref_notification_new_backup_codes' => 'New backup codes have been generated',
|
||||
'pref_notifications' => 'Notifications',
|
||||
'pref_notifications_help' => 'Indicate if these are notifications you would like to get. Some notifications may contain sensitive financial information.',
|
||||
'slack_webhook_url' => 'Slack Webhook URL',
|
||||
@ -2492,6 +2499,7 @@ return [
|
||||
'owner_notification_check_new_version' => 'A new version is available',
|
||||
'owner_notification_check_invite_created' => 'A user is invited to Firefly III',
|
||||
'owner_notification_check_invite_redeemed' => 'A user invitation is redeemed',
|
||||
'owner_notification_check_unknown_user_attempt' => 'An unknown user tries to login',
|
||||
'all_invited_users' => 'All invited users',
|
||||
'save_notification_settings' => 'Save settings',
|
||||
'notification_settings' => 'Settings for notifications',
|
||||
|
3
resources/views/emails/owner/unknown-user.blade.php
Normal file
3
resources/views/emails/owner/unknown-user.blade.php
Normal file
@ -0,0 +1,3 @@
|
||||
@component('mail::message')
|
||||
{{ trans('email.unknown_user_body', ['address' => $address]) }}
|
||||
@endcomponent
|
Loading…
Reference in New Issue
Block a user