Add logging of auth proxy user

This commit is contained in:
Bubka 2024-04-24 14:06:15 +02:00
parent e498350f62
commit 76c3b6fe0c
8 changed files with 37 additions and 18 deletions

View File

@ -16,6 +16,8 @@ use Illuminate\Validation\ValidationException;
class RemoteUserProvider implements UserProvider
{
const FAKE_REMOTE_DOMAIN = '@remote';
/**
* The currently authenticated user.
*
@ -85,7 +87,7 @@ class RemoteUserProvider implements UserProvider
*/
protected function fakeRemoteEmail(mixed $id)
{
return substr($id, 0, 184) . '@remote';
return substr($id, 0, 184) . self::FAKE_REMOTE_DOMAIN;
}
/**

View File

@ -23,8 +23,7 @@ class LogUserLastSeen
// We do not track activity of:
// - Guest
// - User authenticated against a bearer token
// - User authenticated via a reverse-proxy
if (Auth::guard($guard)->check() && ! $request->bearerToken() && config('auth.defaults.guard') !== 'reverse-proxy-guard') {
if (Auth::guard($guard)->check() && ! $request->bearerToken()) {
Auth::guard($guard)->user()->last_seen_at = Carbon::now()->format('Y-m-d H:i:s');
Auth::guard($guard)->user()->save();
break;

View File

@ -1,8 +1,9 @@
<?php
namespace App\Authentication\Listeners;
namespace App\Listeners\Authentication;
use App\Events\VisitedByProxyUser;
use App\Extensions\RemoteUserProvider;
use App\Listeners\Authentication\AbstractAccessListener;
use App\Notifications\SignedInWithNewDevice;
use Illuminate\Support\Carbon;
@ -28,15 +29,17 @@ class VisitedByProxyUserListener extends AbstractAccessListener
$userAgent = $this->request->userAgent();
$known = $user->authentications()->whereIpAddress($ip)->whereUserAgent($userAgent)->whereLoginSuccessful(true)->first();
$newUser = Carbon::parse($user->{$user->getCreatedAtColumn()})->diffInMinutes(Carbon::now()) < 1;
$guard = config('auth.defaults.guard');
$log = $user->authentications()->create([
'ip_address' => $ip,
'user_agent' => $userAgent,
'login_at' => now(),
'ip_address' => $ip,
'user_agent' => $userAgent,
'login_at' => now(),
'login_successful' => true,
'guard' => $guard,
]);
if (! $known && ! $newUser && $user->preferences['notifyOnNewAuthDevice']) {
if (! $known && ! $newUser && ! str_ends_with($user->email, RemoteUserProvider::FAKE_REMOTE_DOMAIN) && $user->preferences['notifyOnNewAuthDevice']) {
$user->notify(new SignedInWithNewDevice($log));
}
}

View File

@ -2,6 +2,7 @@
namespace App\Providers;
use App\Listeners\Authentication\VisitedByProxyUserListener;
use App\Events\GroupDeleted;
use App\Events\GroupDeleting;
use App\Events\VisitedByProxyUser;
@ -65,9 +66,9 @@ class EventServiceProvider extends ServiceProvider
Logout::class => [
LogoutListener::class,
],
// VisitedByProxyUser::class => [
// ProxyUserAccessListener::class,
// ],
VisitedByProxyUser::class => [
VisitedByProxyUserListener::class,
],
];
/**

View File

@ -5,6 +5,7 @@
namespace App\Services\Auth;
use App\Events\VisitedByProxyUser;
use Illuminate\Auth\GuardHelpers;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\UserProvider;
@ -17,14 +18,12 @@ class ReverseProxyGuard implements Guard
/**
* The currently authenticated user.
*
* @var \Illuminate\Contracts\Auth\Authenticatable|null
* @var \Illuminate\Contracts\Auth\Authenticatable|\App\Models\User|null
*/
protected $user;
/**
* Create a new authentication guard.
*
* @return void
*/
public function __construct(UserProvider $provider)
{
@ -76,7 +75,13 @@ class ReverseProxyGuard implements Guard
}
}
return $this->user = $this->provider->retrieveById($identifier);
if ($this->user = $this->provider->retrieveById($identifier)) {
if ($this->user->lastLoginAt() < now()->subMinutes(15)) {
event(new VisitedByProxyUser($this->user));
}
}
return $this->user;
}
/**

View File

@ -7,6 +7,7 @@
import { UseColorMode } from '@vueuse/components'
const notify = useNotifyStore()
const $2fauth = inject('2fauth')
const props = defineProps({
userId: [Number, String],
@ -171,6 +172,7 @@
<div>
<span v-if="isFailedEntry(authentication)" v-html="$t('admin.failed_login_on', { login_at: authentication.login_at })" />
<span v-else-if="isSuccessfulLogout(authentication)" v-html="$t('admin.successful_logout_on', { login_at: authentication.logout_at })" />
<span v-else-if="$2fauth.config.proxyAuth" v-html="$t('admin.viewed_on', { login_at: authentication.login_at })" />
<span v-else v-html="$t('admin.successful_login_on', { login_at: authentication.login_at })" />
</div>
<div>

View File

@ -11,6 +11,7 @@
const router = useRouter()
const user = useUserStore()
const bus = useBusStore()
const $2fauth = inject('2fauth')
const isFetching = ref(false)
const managedUser = ref(null)
@ -212,12 +213,14 @@
<div class="block is-size-6 is-size-7-mobile has-text-grey">
{{ $t('admin.registered_on_date', { date: managedUser.info.created_at }) }} - {{ $t('admin.last_seen_on_date', { date: managedUser.info.last_seen_at }) }}
</div>
<!-- isAdmin option -->
<div class="block">
<!-- otp as dot -->
<FormCheckbox v-model="managedUser.info.is_admin" @update:model-value="val => saveAdminRole(val === true)" fieldName="is_admin" label="admin.forms.is_admin.label" help="admin.forms.is_admin.help" />
</div>
<h2 class="title is-4 has-text-grey-light">{{ $t('admin.access') }}</h2>
<div class="block">
<h2 v-if="!$2fauth.config.proxyAuth" class="title is-4 has-text-grey-light">{{ $t('admin.access') }}</h2>
<!-- access -->
<div v-if="!$2fauth.config.proxyAuth" class="block">
<!-- reset password -->
<div class="list-item is-size-6 is-size-6-mobile has-text-grey">
<div class="mb-3 is-flex is-justify-content-space-between">
<div>
@ -245,6 +248,7 @@
<span v-html="$t('admin.reset_password_help')" class="is-block block"></span>
</div>
</div>
<!-- personal access tokens -->
<div class="list-item is-size-6 is-size-6-mobile has-text-grey is-flex is-justify-content-space-between">
<div>
<span class="has-text-weight-bold">{{ $t('settings.personal_access_tokens') }}</span>
@ -263,6 +267,7 @@
</div>
</div>
</div>
<!-- webauthn devices -->
<div class="list-item is-size-6 is-size-6-mobile has-text-grey is-flex is-justify-content-space-between">
<div>
<span class="has-text-weight-bold">{{ $t('auth.webauthn.security_devices') }}</span>
@ -282,6 +287,7 @@
</div>
</div>
</div>
<!-- last access -->
<div class="block">
<h3 class="title is-5 has-text-grey-light mb-2">{{ $t('admin.last_accesses') }}</h3>
<AccessLogViewer :userId="props.userId" :lastOnly="true" @has-more-entries="showFullLogLink = true"/>

View File

@ -71,6 +71,7 @@ return [
'successful_login_on' => 'Successful login on <span class="light-or-darker">:login_at</span>',
'successful_logout_on' => 'Successful logout on <span class="light-or-darker">:login_at</span>',
'failed_login_on' => 'Failed login on <span class="light-or-darker">:login_at</span>',
'viewed_on' => 'Viewed on <span class="light-or-darker">:login_at</span>',
'last_accesses' => 'Last accesses',
'see_full_log' => 'See full log',
'browser_on_platform' => ':browser on :platform',