agent foundation

This commit is contained in:
2026-03-06 00:44:52 +01:00
parent 9819cba733
commit 9a08f96c11
199 changed files with 8522 additions and 1880 deletions

View File

@@ -9,6 +9,8 @@ use MintyPHP\Service\Auth\ApiTokenService;
use MintyPHP\Service\Auth\AuthScopeGateway;
use MintyPHP\Service\User\UserTenantContextService;
// Static facade for API authentication — state is set once per request during ApiBootstrap::init().
// Dependencies are injected as lazy resolvers so services are only instantiated when actually needed.
class ApiAuth
{
/** @var (callable(): AuthScopeGateway)|null */
@@ -58,6 +60,7 @@ class ApiAuth
{
$header = trim((string) ($_SERVER['HTTP_AUTHORIZATION'] ?? ''));
if ($header === '') {
// Nginx with PHP-FPM may expose the header under this key instead
$header = trim((string) ($_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ?? ''));
}
if ($header === '' || stripos($header, 'Bearer ') !== 0) {
@@ -100,9 +103,11 @@ class ApiAuth
$roleIds = $userRoleRepository->listRoleIdsByUserId($userId);
$permissions = $rolePermissionRepository->listPermissionKeysByRoleIds($roleIds);
// Resolve tenant context
// Resolve tenant context: a token may be scoped to a specific tenant,
// otherwise fall back to the user's active tenant context.
$tokenTenantId = $result['tenant_id'];
if ($tokenTenantId !== null) {
// Verify the token's tenant is still assigned to this user
$userTenantIds = $scopeGateway->getUserTenantIds($userId);
if (!in_array($tokenTenantId, $userTenantIds, true)) {
return false;
@@ -153,11 +158,14 @@ class ApiAuth
return $decision->isAllowed();
}
// Effective tenant for data filtering — may come from token scope or user session.
public static function tenantId(): ?int
{
return self::$currentTenantId;
}
// Non-null only when the token itself was issued for a specific tenant.
// Used to restrict access in token-scoped requests (e.g. tenant integrations).
public static function scopedTenantId(): ?int
{
return self::$tokenTenantId;
@@ -204,6 +212,7 @@ class ApiAuth
$scopeGateway = self::scopeGateway();
if (!$scopeGateway->resourceBelongsToTenant($resource, $resourceId, $tenantId)) {
// 404 instead of 403 — don't reveal that the resource exists in another tenant
ApiResponse::notFound();
}
}