diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 9d44665779d0..64dbfd4b823b 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -13,6 +13,7 @@ namespace App\Http\Controllers\Auth; use App\DataMapper\Analytics\LoginFailure; use App\DataMapper\Analytics\LoginSuccess; +use App\Events\User\UserLoggedIn; use App\Http\Controllers\BaseController; use App\Http\Controllers\Controller; use App\Jobs\Account\CreateAccount; @@ -24,6 +25,7 @@ use App\Models\CompanyToken; use App\Models\CompanyUser; use App\Models\User; use App\Transformers\CompanyUserTransformer; +use App\Utils\Ninja; use App\Utils\Traits\UserSessionAttributes; use Google_Client; use Illuminate\Foundation\Auth\AuthenticatesUsers; @@ -170,6 +172,8 @@ class LoginController extends BaseController $user = $this->guard()->user(); + event(new UserLoggedIn($user, $user->account->default_company, Ninja::eventVars($user->id))); + //if user has 2fa enabled - lets check this now: if($user->google_2fa_secret && $request->has('one_time_password')) diff --git a/app/Http/Middleware/TokenAuth.php b/app/Http/Middleware/TokenAuth.php index b93f7c90479e..bcea2cb8d26e 100644 --- a/app/Http/Middleware/TokenAuth.php +++ b/app/Http/Middleware/TokenAuth.php @@ -68,8 +68,6 @@ class TokenAuth //stateless, don't remember the user. auth()->login($user, false); - event(new UserLoggedIn($user, $company_token->company, Ninja::eventVars())); - } else { $error = [ 'message' => 'Invalid token', diff --git a/app/Listeners/User/UpdateUserLastLogin.php b/app/Listeners/User/UpdateUserLastLogin.php index 6bb1d37d11ff..ed833a9cbac1 100644 --- a/app/Listeners/User/UpdateUserLastLogin.php +++ b/app/Listeners/User/UpdateUserLastLogin.php @@ -11,7 +11,10 @@ namespace App\Listeners\User; +use App\Jobs\Mail\NinjaMailerJob; +use App\Jobs\Mail\NinjaMailerObject; use App\Libraries\MultiDB; +use App\Mail\User\UserLoggedIn; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Events\Dispatchable; @@ -38,11 +41,29 @@ class UpdateUserLastLogin implements ShouldQueue */ public function handle($event) { + MultiDB::setDb($event->company->db); $user = $event->user; - $user->last_login = now(); $user->save(); + + $event_vars = $event->event_vars; + $ip = array_key_exists('ip', $event->event_vars) ? $event->event_vars['ip'] : 'IP address not resolved'; + + if($user->ip != $ip) + { + $nmo = new NinjaMailerObject; + $nmo->mailable = new UserLoggedIn($user, $user->account->companies()->first(), $ip); + $nmo->company = $user->account->companies()->first(); + $nmo->settings = $user->account->companies()->first()->settings; + $nmo->to_user = $user; + NinjaMailerJob::dispatch($nmo); + + $user->ip = $ip; + $user->save(); + } + + } } diff --git a/app/Mail/Import/ImportCompleted.php b/app/Mail/Import/ImportCompleted.php index ecb50e51487f..8396fda29903 100644 --- a/app/Mail/Import/ImportCompleted.php +++ b/app/Mail/Import/ImportCompleted.php @@ -1,4 +1,13 @@ whitelabel = $this->company->account->isPaid(); return $this->from(config('mail.from.address'), config('mail.from.name')) + ->subject(ctrans('texts.max_companies')) ->view('email.migration.max_companies'); } } diff --git a/app/Mail/User/UserLoggedIn.php b/app/Mail/User/UserLoggedIn.php new file mode 100644 index 000000000000..7eade000111e --- /dev/null +++ b/app/Mail/User/UserLoggedIn.php @@ -0,0 +1,59 @@ +company = $company; + $this->user = $user; + $this->ip = $ip; + } + + /** + * Build the message. + * + * @return $this + */ + public function build() + { + + return $this->from(config('mail.from.address'), config('mail.from.name')) + ->subject(ctrans('texts.new_login_detected')) + ->view('email.admin.notification') + ->with([ + 'settings' => $this->company->settings, + 'logo' => $this->company->present()->logo(), + 'title' => ctrans('texts.new_login_detected'), + 'body' => ctrans('texts.new_login_description', ['email' =>$this->user->email, 'ip' => $this->ip, 'time' => now()]), + 'whitelabel' => $this->company->account->isPaid(), + ]); + } +} diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index a10ad715f272..2a46a25013ba 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -4246,6 +4246,8 @@ $LANG = array( 'activity_102' => ':user archived recurring invoice :recurring_invoice', 'activity_103' => ':user deleted recurring invoice :recurring_invoice', 'activity_104' => ':user restored recurring invoice :recurring_invoice', + 'new_login_detected' => 'New login detected for your account.', + 'new_login_description' => 'You recently logged in to your Invoice Ninja account from a new location or device:

IP: :ip
Time: :time
Email: :email', ); return $LANG; diff --git a/resources/views/email/admin/notification.blade.php b/resources/views/email/admin/notification.blade.php new file mode 100644 index 000000000000..7565faf86e21 --- /dev/null +++ b/resources/views/email/admin/notification.blade.php @@ -0,0 +1,18 @@ +@component('email.template.master', ['design' => 'light', 'settings' => $settings]) + + @slot('header') + @include('email.components.header', ['logo' => $logo]) + @endslot + +

{!! $title !!}

+ +

{!! $body !!}

+ + @if(isset($whitelabel) && !$whitelabel) + @slot('footer') + @component('email.components.footer', ['url' => 'https://invoiceninja.com', 'url_text' => '© InvoiceNinja']) + For any info, please visit InvoiceNinja. + @endcomponent + @endslot + @endif +@endcomponent