Browse Source

Add logging of auth proxy user

Bubka 1 year ago
parent
commit
76c3b6fe0c

+ 3 - 1
app/Extensions/RemoteUserProvider.php

@@ -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;
     }
 
     /**

+ 1 - 2
app/Http/Middleware/LogUserLastSeen.php

@@ -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;

+ 8 - 5
app/Listeners/Authentication/VisitedByProxyUserListener.php

@@ -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));
         }
     }

+ 4 - 3
app/Providers/EventServiceProvider.php

@@ -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,
+        ],
     ];
 
     /**

+ 9 - 4
app/Services/Auth/ReverseProxyGuard.php

@@ -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;
     }
 
     /**

+ 2 - 0
resources/js/components/AccessLogViewer.vue

@@ -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>

+ 9 - 3
resources/js/views/admin/users/Manage.vue

@@ -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"/>

+ 1 - 0
resources/lang/en/admin.php

@@ -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',