74 lines
2.6 KiB
PHP
74 lines
2.6 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Graph;
|
|
|
|
use Illuminate\Http\Client\RequestException;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\Http;
|
|
use RuntimeException;
|
|
|
|
/**
|
|
* Client Microsoft Graph minimal (flux client credentials, permissions « application »).
|
|
* Permissions requises, avec consentement administrateur : User.Read.All, GroupMember.Read.All.
|
|
*/
|
|
class GraphClient
|
|
{
|
|
public function __construct(private readonly string $tenantId) {}
|
|
|
|
public function token(): string
|
|
{
|
|
return Cache::remember("graph-token:{$this->tenantId}", now()->addMinutes(50), function () {
|
|
$res = Http::asForm()->timeout(config('signature.graph.timeout'))
|
|
->post("https://login.microsoftonline.com/{$this->tenantId}/oauth2/v2.0/token", [
|
|
'client_id' => config('signature.graph.client_id'),
|
|
'client_secret' => config('signature.graph.client_secret'),
|
|
'scope' => 'https://graph.microsoft.com/.default',
|
|
'grant_type' => 'client_credentials',
|
|
]);
|
|
if (! $res->successful()) {
|
|
throw new RuntimeException('Jeton Graph refusé : '.$res->json('error_description', $res->body()));
|
|
}
|
|
|
|
return $res->json('access_token');
|
|
});
|
|
}
|
|
|
|
/** GET sur une URL relative (/users…) ou absolue (@odata.nextLink). */
|
|
public function get(string $url, array $headers = []): array
|
|
{
|
|
$full = str_starts_with($url, 'https://') ? $url : config('signature.graph.base_url').$url;
|
|
|
|
$res = Http::withToken($this->token())
|
|
->withHeaders($headers)
|
|
->timeout(config('signature.graph.timeout'))
|
|
// Nouvel essai sur limitation (429) ou indisponibilité temporaire de Graph.
|
|
->retry(3, 2000, fn ($e) => $e instanceof RequestException && in_array($e->response->status(), [429, 503, 504], true), throw: false)
|
|
->get($full);
|
|
|
|
if (! $res->successful()) {
|
|
throw new RuntimeException("Graph {$res->status()} sur {$url} : ".$res->body());
|
|
}
|
|
|
|
return $res->json();
|
|
}
|
|
|
|
/**
|
|
* Parcourt toutes les pages d'une collection.
|
|
* Retourne [éléments, deltaLink|null].
|
|
*/
|
|
public function all(string $url, array $headers = []): array
|
|
{
|
|
$items = [];
|
|
$delta = null;
|
|
$next = $url;
|
|
while ($next) {
|
|
$page = $this->get($next, $headers);
|
|
array_push($items, ...($page['value'] ?? []));
|
|
$next = $page['@odata.nextLink'] ?? null;
|
|
$delta = $page['@odata.deltaLink'] ?? $delta;
|
|
}
|
|
|
|
return [$items, $delta];
|
|
}
|
|
}
|