Security
Impersonation
Let trusted users act as another user to reproduce a problem, with clear rules for who may impersonate whom.
Impersonation lets one user see the app as another user. Support staff use it to reproduce a problem. Nothing is on by default: nobody may impersonate until you say who.
Turn it on
Add the Impersonatable trait to your user model, and override canImpersonate() for the users you trust:
use Illuminate\Foundation\Auth\User as Authenticatable;
use Lathe\Auth\Impersonatable;
class User extends Authenticatable
{
use Impersonatable;
public function canImpersonate(): bool
{
return $this->is_admin;
}
}
The trait gives canImpersonate() a default of false, so adding it alone opens nothing. It also gives canBeImpersonated() a default of true.
The Resource for the user model then shows "Impersonate" in the row menu and in the detail menu. It shows only for a user who may impersonate that row.
Decide who can be impersonated
Override canBeImpersonated() to protect some users. This keeps administrators from being impersonated:
public function canBeImpersonated(): bool
{
return ! $this->is_admin;
}
A user may start impersonating when all of these hold:
- Both the user and the target use the
Impersonatabletrait. - They are two different people. Impersonating yourself answers 403 with "You cannot impersonate yourself."
- The user's
canImpersonate()returnstrue. - The target's
canBeImpersonated()returnstrue. - No impersonation is running.
If one fails, the request answers 403 and no menu entry shows.
The panel finds the target through the scoped query of the user Resource. A user that Resource cannot reach, such as one from another tenant, answers 404. See Tenancy.
Start and stop
Pick "Impersonate" and the panel signs you in as the target. A bar at the top of every page says "You are currently impersonating" and the user's name, with a "Stop Impersonating" button. Press it and you are signed in as yourself again, with the same "remember me" choice you had.
The start goes to the path in lathe.impersonation.started. A stop goes back to the page where you started, if that page is on the same site. Otherwise it goes to lathe.impersonation.stopped. Both default to / and can be set in .env:
LATHE_IMPERSONATION_STARTED=/dashboard
LATHE_IMPERSONATION_STOPPED=/lathe
Lathe checks the saved page before it uses it. A relative path or a URL on your own host is fine. Anything that points to another host, such as //evil.test, is ignored.
If the user who started the session no longer exists, a stop clears the impersonation and does not sign anyone in.
Events and the audit log
Two events fire, both from Lathe\Events: ImpersonationStarted and ImpersonationStopped. Each carries the impersonator, the impersonated user and the redirectTo path. Listen for them to notify someone or write your own log:
use Illuminate\Support\Facades\Event;
use Lathe\Events\ImpersonationStarted;
Event::listen(ImpersonationStarted::class, function (ImpersonationStarted $event) {
logger()->warning('Impersonation started', [
'by' => $event->impersonator->getKey(),
'as' => $event->impersonated->getKey(),
]);
});
The panel also records both events in the audit log with the impersonator as the actor. See Audit log.
Check the state in code
Lathe::impersonator() returns the object that does the work. It binds the Lathe\Auth\Contracts\ImpersonatesUsers contract in the container. Ask it whether the request is impersonating:
use Lathe\Lathe;
if (Lathe::impersonator()->impersonating($request)) {
// Hide a button, or mark an export.
}
The panel also answers GET /lathe/api/impersonate with {"impersonating": true} or false.
Replace the implementation
Bind your own class to ImpersonatesUsers to change how the session is kept. The contract has six methods: impersonate(), stopImpersonating(), impersonating(), flushImpersonationData(), redirectAfterStartingImpersonation() and redirectAfterStoppingImpersonation().
$this->app->bind(\Lathe\Auth\Contracts\ImpersonatesUsers::class, \App\Auth\MyImpersonation::class);
The checks for who may impersonate whom stay in the controller. They do not move with your class.
What you see
For a user who may impersonate, the Users Resource shows an "Impersonate" entry in the row menu and the detail menu. After you pick it, a bar across the top says who you are acting as and offers "Stop Impersonating". Pressing it returns you to the page you started from.