Logging impersonation data

Two Laravel packages I use often are packages to impersonate a user and packages to log activity (including model changes).

Both are useful, but there’s typically one piece missing. Activity log packages will log *who* initiated a change, but not who the person may have been impersonated by. If I log in as me, then impersonate user X, then make changes, the activity log will typically just show ‘user X made these changes’. However, it was really *me* doing it on behalf of person X.

There’s a few ways one could handle this, and I’d initially looked at request middleware (and still might move in that direction), but with the Spatie activitylog package, I added a ‘pipe’ during the call to initialize logging options.

Here’s a bit of code I use with the Spatie Activitylog package and the laravel-impersonate package.

<?php

namespace App\Services\ActivityLog\Pipes;

use Closure;
use Spatie\Activitylog\Contracts\LoggablePipe;
use Spatie\Activitylog\EventLogBag;

class AddImpersonatorPipe implements LoggablePipe
{
    public function __construct(protected string $field)
    {
    }

    public function handle(EventLogBag $event, Closure $next): EventLogBag
    {
        $manager = app('impersonate');
        if ($manager->isImpersonating()) {
            $impersonatorId = $manager->getImpersonatorId();
            $event->changes[$this->field] = $impersonatorId;
        }

        return $next($event);
    }
}

As you can see, I’m simply adding the originating impersonator ID to the ‘change’ list. It’s not a separated activity log property, nor a separate column in the database table where logged activity lives. That’s certainly possible, but I was trying to handle this without any database changes at this point.

On each model I log changes on, I have a ‘getActivitylogOptions’ method (via a trait) that adds the pipe code above to the logger’s pipeline.

    public function getActivitylogOptions(): LogOptions
    {
        self::addLogChange(new AddImpersonatorPipe('impersonator_id'));

        return LogOptions::defaults()
            ->logAll();
    }

What I’ve not tested yet is if non-model ‘activity’ log behaviour would still have the impersonation processing in place if it’s no model is invoked in the request. For example, just logging that an api call was made. However, pretty much every request has at least the User model invoked, which has the getActivitylogOptions() call in place, so I’m not sure there’s ever a time that would not be invoked.

Also… reviewing this out loud… the self::addLogChange() call may be being invoked once per object instantiation (or at least initial model instantiation) which may be redundant. I’ll need to investigate more.

There doesn’t seem to be a standard process for this, but it’s definitely useful information to log, even if you choose to log it a separate way.

Similar Posts

  • A bit of feedback…

    A small bit of feedback… that’s often what a mobile user is looking for. Haptic feedback – a quick device vibration – is great. It’s subtle, quick, doesn’t interrupt, but gives an actual *feeling* that something happened. And… on iOS, it’s harder to do without building a full ‘native mobile app’. iOS Safari doesn’t support…

  • Open Source TechFinder

    Inspired by the AUTM conference, I got inspired to look at some of the common processes techtransfer folks do. Main idea was to try to develop something relatively ‘standalone’ that might address a use case I learned about, so I decided on building a web-based open source techfinder tool to publish licensable technologies. The notion…

  • Identity and habits

    “Your present identity should not constrain your future habits”. For this quote from the audio book “Hello, Habits“. Obviously the book is about ‘habits’, but the phrase could easily have been “Your present identity should not constrain your future self”. In either reading of this, it’s been stuck in my head for while now.  How…

  • MySQL speed boost

    I hit a problem the other day with concurrent queries causing deadlocks.  Using innodb gives you a lot of protection with respect to transaction support, but it carries a moderate amount of overhead, and unless you’re aware of what’s going on, you may be paying a higher price which can eventually cause performance or deadlock…

Leave a Reply

Your email address will not be published. Required fields are marked *