mirror of
https://github.com/projectsend/projectsend.git
synced 2026-09-16 16:45:07 +00:00
91d34b204c
An actor with no personal access token has always meant a browser, and for as long as a session and a Sanctum token were the only two ways to authenticate, that was true. It stops being true the moment anything else can, and the failure is silent: the action gets recorded as a person clicking, in the one table whose whole purpose is answering "did I do that, or did something acting for me?" Nothing misreports today — every call site that passes an explicit actor is a browser request, an API request whose actor carries the token, or a console command with no actor at all. This closes the trap before the AI connector in cloud-modules walks into it. ActivityOrigin is a closed enum, so core has to publish both the case and the hook before a package can use either. ResolvingActivityOrigin is asked only in the ambiguous case: a request carrying a token is the API and a request with nobody signed in is public or system, and neither is in any doubt, so neither is offered — one package must not be able to quietly relabel how every integration's actions are attributed. The person stays the actor. They authorised it, and a log naming the assistant instead would lose the only fact that matters. What the connector was called goes in api_token_name, beside a null token id, because that column means a row in personal_access_tokens and this is not one. The new origin is kept out of the activity filter unless the edition can actually produce it. A filter option that can only ever return nothing is a feature dangled at an edition that does not have it, which is the one thing the edition boundary exists not to do.
54 lines
1.9 KiB
PHP
54 lines
1.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Modules\Audit\Events;
|
|
|
|
use App\Models\User;
|
|
use App\Modules\Audit\ActivityOrigin;
|
|
|
|
/**
|
|
* "This request has a signed-in actor and no personal access token — was
|
|
* it really a browser?"
|
|
*
|
|
* Asked only in that one ambiguous case. A request carrying a Sanctum
|
|
* token is the API, a request with nobody signed in is public or system,
|
|
* and neither is in any doubt — so neither is offered here.
|
|
*
|
|
* The doubt exists because "no token" has always meant "a session", and
|
|
* that stops being true the moment anything else can authenticate a
|
|
* request. `ActivityOrigin` is a closed enum a package cannot extend, so
|
|
* core has to publish both the case and this hook before a package can
|
|
* say "that was mine". Without it a new credential would be recorded as
|
|
* a person clicking in a browser — silently, and in the one table whose
|
|
* whole purpose is answering "did I do that, or did something acting for
|
|
* me?"
|
|
*
|
|
* Set `$origin` only if you recognise the credential on the current
|
|
* request. Leaving it null means "not mine", which is the honest answer
|
|
* for every listener that is not looking at its own guard.
|
|
*
|
|
* Listened to by *string* class name from a package, same as every other
|
|
* hook here — see docs/extension-points-architecture.md.
|
|
*/
|
|
final class ResolvingActivityOrigin
|
|
{
|
|
/**
|
|
* What actually authenticated this request. Null until a listener
|
|
* claims it, after which core stops assuming a browser session.
|
|
*/
|
|
public ?ActivityOrigin $origin = null;
|
|
|
|
/**
|
|
* What to show beside the entry — the name of the connector or
|
|
* application acting, not the person. Snapshotted into the same
|
|
* column an API token's name goes in, for the same reason: revoking
|
|
* the credential must not leave the entry pointing at nothing.
|
|
*/
|
|
public ?string $credentialName = null;
|
|
|
|
public function __construct(
|
|
public readonly User $actor,
|
|
) {}
|
|
}
|