Non puoi selezionare più di 25 argomenti
Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
746 righe
26 KiB
746 righe
26 KiB
<?php |
|
|
|
/** |
|
* Gestore delle API ANAC NPA |
|
*/ |
|
trait API |
|
{ |
|
|
|
/** |
|
* Get the ANAC JWS custom claims |
|
* |
|
* @param String $aud Audience delle claims |
|
* @param ?string $businessFlowID |
|
* @param ?string $userCodiceFiscale Se impostato, forza il codice fiscale |
|
* @param ?string $userRole Se impostato, forza il ruolo utente |
|
* @param ?string $saCodiceFiscale Se impostato, forza il codice fiscale della stazione appaltante |
|
* @param ?string $saCodiceAusa Se impostato, forza il codice ausa della stazione appaltante |
|
* |
|
* @return Array |
|
*/ |
|
private function getAnacJwsCustomClaims( |
|
String $aud, |
|
?string $businessFlowID = null, |
|
?string $userCodiceFiscale = null, |
|
?string $userRole = null, |
|
?string $saCodiceFiscale = null, |
|
?string $saCodiceAusa = null |
|
): array |
|
{ |
|
|
|
global $config; |
|
if (empty($businessFlowID)) { |
|
$businessFlowID = "00000000-0000-0000-0000-000000000000"; |
|
} |
|
|
|
// Gather del codice fiscale |
|
if(empty($userCodiceFiscale)) { |
|
if (defined("__CMDS_JOB_EXEC") && !empty(__CMDS_JOB_EXEC)) { |
|
// Il cron si comporta come RP o come delegato richiesto |
|
$userRole = empty($this->__cronOverrideRole) ? DelegheRoles::RP : $this->__cronOverrideRole; |
|
$userCodiceFiscale = empty($this->__cronOverrideCF) ? $this->getUserFromRole($userRole, RoleSyncBehaviour::NEVER_SYNC) : $this->__cronOverrideCF; |
|
} elseif (!empty($_SESSION["utente"]) && method_exists($_SESSION["utente"], "getInfo")) { |
|
$userCodiceFiscale = $_SESSION["utente"]->getInfo()["cf"]; |
|
} |
|
} |
|
|
|
if(empty($userRole)) { |
|
$userRole = $this->getUserRole($userCodiceFiscale, null, RoleSyncBehaviour::NEVER_SYNC); |
|
} |
|
|
|
// Gather delle info sulla stazione appaltante |
|
if(empty($saCodiceFiscale) && empty($saCodiceAusa)) { |
|
|
|
if(isset($this->overrideStazioneAppaltante)) { |
|
$saCodiceFiscale = $this->overrideStazioneAppaltante["codiceFiscale"]; |
|
$saCodiceAusa = $this->overrideStazioneAppaltante["codiceAusa"]; |
|
} else { |
|
// Proviamo se la delega corrente prevede info della SA |
|
$infoSA = $this->getFullDelegaInfo($userCodiceFiscale); |
|
if(!empty($infoSA) && !empty($infoSA["sa_cf"]) && !empty($infoSA["sa_ausa"])) { |
|
$saCodiceFiscale = $infoSA["sa_cf"]; |
|
$saCodiceAusa = $infoSA["sa_ausa"]; |
|
} else { |
|
// Proviamo poi dal fascicolo |
|
if (!empty($this->fascicolo["data"])) { |
|
$data = unserialize($this->fascicolo["data"]); |
|
} |
|
$saCodiceFiscale = $data["benficiario"]["codice_fiscale"] ?? null; |
|
$saCodiceAusa = $data["benficiario"]["codice_ausa"] ?? null; |
|
// Poi dall'ente |
|
if (isset($_SESSION["utente"]) && isset($_SESSION["ente"]) && (!defined("__CMDS_JOB_EXEC") || empty(__CMDS_JOB_EXEC))) { |
|
$ente = (!empty($_SESSION["utente"]->codice_ente)) ? $_SESSION["utente"]->codice_ente : $_SESSION["ente"]->codice; |
|
if (!empty($ente)) { |
|
$ente = Ente::getInfoFromID($ente); |
|
if (!empty($ente)) { |
|
$ente = reset($ente); |
|
if (!empty($ente["cf"]) && !empty($ente["codice_ausa"]) && $ente["cf"] != $saCodiceFiscale) { |
|
$saCodiceFiscale = $ente["cf"]; |
|
$saCodiceAusa = $ente["codice_ausa"]; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
} else { |
|
if(empty($saCodiceFiscale) || empty($saCodiceAusa)) { |
|
throw new Exception("Se viene specificato il CF della Stazione Appaltante è necessario specificare anche il codice AUSA e viceversa"); |
|
} |
|
} |
|
|
|
// Gathering del codice fiscale e ruolo |
|
|
|
|
|
|
|
|
|
$userCodiceFiscale = trim($userCodiceFiscale); |
|
|
|
|
|
if (!in_array($userRole, DelegheRoles::VALID_ROLES)) { |
|
return []; |
|
} |
|
|
|
$userLoa = $_SESSION["utente"]->authenticationLevel + 1; |
|
|
|
|
|
return [ |
|
|
|
"userCodiceFiscale" => $userCodiceFiscale, |
|
// codice fiscale dell'utente connesso alla piattaforma e operante per conto della Stazione appaltante |
|
|
|
"userRole" => $userRole, |
|
// ruolo dell'utente. RP = Responsabile del Porgetto; DRP = Delegato del Responsabile del Porgetto [RP, DRP] |
|
|
|
"userLoa" => "{$userLoa}", |
|
// level of assurance adottato dalla piattaforma per l'identificazione dell'utente [3, 4] |
|
|
|
"userIdpType" => (!empty($_SESSION["utente"]->publicSSO)) ? $_SESSION["utente"]->publicSSO : "CUSTOM", |
|
// tipo di identity provider utilizzato per stabilire l'identità dell'utente. |
|
"SACodiceFiscale" => DEVELOP_ENV && isset($config["DEV"]["SACodiceFiscale"]) ? $config["DEV"]["SACodiceFiscale"] : ($saCodiceFiscale), |
|
// codice Fiscale della stazione appaltante. Può essere nullo in caso di soggetti non dotati di personalità giuridica |
|
|
|
"SAcodiceAUSA" => DEVELOP_ENV && isset($config["DEV"]["SACodiceAusa"]) ? $config["DEV"]["SACodiceAusa"] : ($saCodiceAusa), |
|
|
|
// codice ausa della stazione appaltante alla quale appartiene l'utente |
|
|
|
"regCodicePiattaforma" => $config["pdnd"]["platform"]["regCodicePiattaforma"], |
|
// codice identificativo della piattaforma |
|
|
|
"regCodiceComponente" => $config["pdnd"]["platform"]["regCodiceComponente"], |
|
// codice identificativo del componente client |
|
|
|
"businessFlowID" => $businessFlowID, |
|
// coincide con idAppalto. Assume valore "00000000-0000-0000-0000-000000000000" nella prima transazione (che è necessariamente comunicaAppalto.crea-appalto) In tutte le operazioni successive riconduce la transazione all’appalto |
|
|
|
"traceID" => \Ramsey\Uuid\Uuid::uuid6()->toString(), |
|
// identificativo univoco assegnato dalla piattaforma alla richiesta |
|
|
|
"spanID" => \Ramsey\Uuid\Uuid::uuid6()->toString(), |
|
// identificativo univoco assegnato dalla piattaforma (?) all'operazione iniziale richiesta dall'utente |
|
|
|
"aud" => $aud, |
|
// Audience |
|
]; |
|
} |
|
|
|
|
|
|
|
/** |
|
* Effettua una chiamata API verso NPA |
|
* |
|
* @param String $apiEndpoint Endpoint da contattare |
|
* @param String $method Metodo da contattare |
|
* @param array $body Corpo della richiesta |
|
* @param array|null $claims Claim per la generazione dei voucher |
|
* @param array|null $header |
|
* @param string|null $message Viene popolato con u neventuale messaggio d'errore |
|
* @param int $retry401 Viene usato per tenere traccia se la richiesta corrente è un tentativo per autenticazione fallita |
|
* @return String |
|
*/ |
|
public function apiCall(String $apiEndpoint, String $method, array $body, ?array $claims = null, ?array $header = [], ?string &$message = null, int $retry401 = Retries401::NORMAL): String |
|
{ |
|
// Cerchiamo le config |
|
$endpointConfig = $this->api_config[$apiEndpoint] ?? null; |
|
$methodConfig = $endpointConfig["methods"][$method] ?? null; |
|
|
|
if ($endpointConfig == null || $methodConfig == null) { |
|
throw new Exception("Chiamata API {$apiEndpoint}::{$method} non trovata."); |
|
} |
|
|
|
// Verifichiamo che l'utente possa fare la chiamata |
|
$canDoRequest = $this->canDoRequest($reason, $apiEndpoint, $method); |
|
if (!$canDoRequest) { |
|
$message = $reason["msg"]; |
|
if ($methodConfig["log_auth_failures"] ?? true) { |
|
$this->appendLog( |
|
$apiEndpoint, |
|
$method, |
|
["canDoRequest" => false], |
|
is_array($reason) ? json_encode($reason) : $reason, |
|
); |
|
} |
|
return false; |
|
} |
|
|
|
$saveLog = $methodConfig["log"] ?? true; |
|
|
|
$url = $endpointConfig["endpoint"] . "/" . $methodConfig["path"]; |
|
$request_method = $methodConfig["type"] ?? "GET"; |
|
|
|
$ch = curl_init(); |
|
|
|
$url = ltrim($url, "/"); |
|
|
|
if ($request_method == "POST") { |
|
|
|
$send_header = array_merge(["Content-Type: application/json"], $header); |
|
|
|
$payload = json_encode($body); |
|
curl_setopt($ch, CURLOPT_URL, $url); |
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); |
|
|
|
} else { |
|
|
|
$send_header = $header; |
|
$query = ""; |
|
if (!empty($body)) { |
|
$query = "?" . http_build_query($body); |
|
} |
|
$url = "{$url}{$query}"; |
|
curl_setopt($ch, CURLOPT_URL, $url); |
|
|
|
} |
|
|
|
// Generazione di un voucher generico |
|
if (empty($claims)) { |
|
$claims = $this->getAnacJwsCustomClaims( |
|
$endpointConfig["endpoint"], |
|
$this->getUUID() |
|
); |
|
} |
|
|
|
|
|
if (isset($claims["userCodiceFiscale"])) { |
|
// Se è già uppercase skippiamo il secondo tentativo |
|
// Altrimenti il primo tentativo lo manda così com'è |
|
if ($retry401 == Retries401::UPPERCASE ) { |
|
$claims["userCodiceFiscale"] = strtoupper($claims["userCodiceFiscale"]); |
|
// Se è il terzo tentativo proviamo tutto lowercase |
|
} elseif ($retry401 == Retries401::LOWERCASE) { |
|
$claims["userCodiceFiscale"] = strtolower($claims["userCodiceFiscale"]); |
|
} |
|
} |
|
|
|
|
|
try { |
|
$authorization = PDND::getVoucher( |
|
$endpointConfig["purpose"], |
|
$claims |
|
); |
|
} catch (\Throwable $t) { |
|
$message = DEVELOP_ENV ? $t->getMessage() : "Impossibile contattare i server PDND. Riprova più tardi."; |
|
$this->appendLog( |
|
$apiEndpoint, |
|
$method, |
|
["pdnd" => true], |
|
"PDND error " . $t->getMessage(), |
|
); |
|
return false; |
|
} |
|
|
|
// Mancanza di voucher |
|
if ($authorization === null || empty($authorization->access_token)) { |
|
$message = "Impossibile creare Voucher PDND"; |
|
$this->appendLog( |
|
$apiEndpoint, |
|
$method, |
|
[ |
|
"pdnd" => true, |
|
], |
|
"Can't create voucher." |
|
); |
|
return false; |
|
} |
|
|
|
$send_header = array_merge([ |
|
"Authorization: Bearer {$authorization->access_token}", |
|
"Agid-JWT-TrackingEvidence: {$authorization->tracking_evidence_jwt}" |
|
], $send_header); |
|
|
|
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $send_header); |
|
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $request_method); |
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); |
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); |
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); |
|
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); |
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE); |
|
curl_setopt($ch, CURLOPT_TIMEOUT, 60); |
|
if ($method === "serviceStatus") { |
|
curl_setopt($ch, CURLOPT_TIMEOUT, 10); |
|
} |
|
curl_setopt($ch, CURLOPT_MAXREDIRS, 10); |
|
curl_setopt($ch, CURLOPT_ENCODING, 'UTF-8'); |
|
|
|
ob_start(); |
|
$verbose = fopen('php://temp', 'w+'); |
|
curl_setopt($ch, CURLOPT_VERBOSE, true); |
|
curl_setopt($ch, CURLOPT_STDERR, $verbose); |
|
|
|
try { |
|
|
|
$result = curl_exec($ch); |
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); |
|
if (isset($claims["userCodiceFiscale"])) { |
|
if($httpCode < 400 && in_array($apiEndpoint, ["pianificazioneAppalto", "comunicaAppalto", "comunicaPostPubblicazione"], true)) { |
|
$delegaAttuale = $this->getFullDelegaInfo($claims["userCodiceFiscale"]); |
|
// Se non c'è delega, possiamo considerarlo il nuovo RP essendo il primo submit |
|
if(empty($delegaAttuale) || empty($delegaAttuale["role"])) { |
|
$role = DelegheRoles::RP; |
|
} else { |
|
$role = $delegaAttuale["role"]; |
|
} |
|
// Aggiorniamo la delega |
|
self::setUserRoleToDB($claims["userCodiceFiscale"], $role, $this->fascicolo["codice"], $claims["SACodiceFiscale"], $claims["SAcodiceAUSA"]); |
|
} |
|
|
|
} |
|
|
|
|
|
// Detect 401 |
|
$is401 = false; |
|
if ($retry401 < Retries401::MAX) { |
|
$is401 = |
|
empty($result) || |
|
($httpCode == 401 && is_string($result) && str_contains($result, "SEC05")) || |
|
($httpCode == 400 && is_string($result) && str_contains($result, "AUT01")); |
|
} |
|
|
|
rewind($verbose); |
|
$verbose = stream_get_contents($verbose); |
|
|
|
// Workaround per case del codice fiscale |
|
if ($is401) { |
|
// Riproviamo |
|
if ($saveLog) { |
|
$this->appendLog( |
|
$apiEndpoint, |
|
$method, |
|
[ |
|
"request" => $verbose, |
|
"payload" => $payload ?? null, |
|
"result" => $result ?? null, |
|
"attempt" => $retry401, |
|
] |
|
); |
|
} |
|
return $this->apiCall($apiEndpoint, $method, $body, $claims, $header, $message, $retry401 + 1); |
|
} |
|
// Estriamo il transaction ID |
|
if (!empty($this->scheda["codice"])) { |
|
|
|
$pattern = '/(GovWay-Transaction-ID:)\s(.+\n)/'; |
|
|
|
preg_match($pattern, $verbose, $matches); |
|
|
|
$transactionId = $matches[2] ?? ""; |
|
if (!empty($transactionId)) { |
|
$infoScheda = new stdClass(); |
|
if (!empty($this->scheda["info"])) |
|
$infoScheda = unserialize($this->scheda["info"]); |
|
$infoScheda->lastTransactionID = $transactionId; |
|
$this->updateScheda(["info" => serialize($infoScheda)]); |
|
} |
|
} |
|
if ($saveLog) { |
|
$this->appendLog( |
|
$apiEndpoint, |
|
$method, |
|
[ |
|
"request" => $verbose, |
|
"payload" => $payload ?? null, |
|
"result" => $result ?? null, |
|
] |
|
); |
|
} |
|
|
|
|
|
curl_close($ch); |
|
|
|
return $result; |
|
} catch (\Exception $e) { |
|
$this->appendLog( |
|
$apiEndpoint, |
|
$method, |
|
[ |
|
"request" => $verbose, |
|
"payload" => $payload ?? null, |
|
"result" => $result ?? null, |
|
], |
|
$e->getMessage(), |
|
); |
|
|
|
return $e->getMessage(); |
|
} |
|
} |
|
|
|
/** |
|
* Ottiene il permesso per una chiamata API |
|
* |
|
* @param string|null $apiEndpoint |
|
* @param string|null $method |
|
* @param boolean|null $dynamicPermission |
|
* @return array |
|
*/ |
|
public function getPermissionFor(?string $apiEndpoint, ?string $method, ?bool &$dynamicPermission = null): array |
|
{ |
|
$permission = null; |
|
|
|
if ($apiEndpoint !== null && $method !== null) { |
|
// Verifichiamo i permessi se necessario |
|
$endpointConfig = $this->api_config[$apiEndpoint] ?? null; |
|
$methodConfig = $endpointConfig["methods"][$method] ?? null; |
|
|
|
// Verifichiamo i permessi, prende priorità quello del metodo |
|
$permission = $methodConfig["permission"] ?? null; |
|
// Il permesso dinamico è in base alla tipologia della scheda |
|
$dynamicPermission = $methodConfig["dynamic_permission"] ?? false; |
|
|
|
// Proviamo altrimenti a prendere i permessi generici della categoria |
|
if ($permission === null) { |
|
$permission = $endpointConfig["permission"] ?? null; |
|
$dynamicPermission = $endpointConfig["dynamic_permission"] ?? false; |
|
} |
|
} |
|
|
|
// Permessi di default |
|
if ($permission === null) { |
|
$permission = DelegheRoles::ANY; |
|
$dynamicPermission = false; |
|
} |
|
return $permission; |
|
} |
|
|
|
/** |
|
* Verifica che l'utente sia abilitato ad effettuare una richiesta |
|
* |
|
* @param array|null $reason Viene popolato con la motivazione per un eventuale diniego |
|
* @param string|null $apiEndpoint API Endpoint da contattare (opzionale) |
|
* @param string|null $method Metodo da contattare (opzionale) |
|
* @return boolean |
|
*/ |
|
public function canDoRequest(?array &$reason = null, ?string $apiEndpoint = null, ?string $method = null) |
|
{ |
|
if ($apiEndpoint === "ausaController" && $method === "getBy") { |
|
return true; |
|
} |
|
|
|
// Siamo un cronjob o simile |
|
$canDoRequest = false; |
|
if (defined("__CMDS_JOB_EXEC") && !empty(__CMDS_JOB_EXEC)) { |
|
return true; |
|
} |
|
// Siamo un utente |
|
$utente = $_SESSION["utente"] ?? null; |
|
|
|
// Metodo per evitare nesting eccessivo |
|
do { |
|
// Verifichiamo il livello di autenticazione |
|
if (!$utente || $utente->authenticationLevel < 2) { |
|
$reason = ["code" => "AUTH"]; |
|
break; |
|
} |
|
|
|
// Sanity check sul RUP |
|
if ($method !== "presa-carico") { |
|
if (isset($this->oggetto) && method_exists($this->oggetto, "getRup")) { |
|
$rp_cf = trim(strtoupper($this->getUserFromRole(DelegheRoles::RP))); |
|
$rup_cf = trim(strtoupper($this->oggetto->getRup()["codice_fiscale"] ?? "")); |
|
if ($rp_cf !== $rup_cf) { |
|
$reason = ["code" => "RUP_RP_MISMATCH"]; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
// Otteniamo le info utente |
|
$userInfo = method_exists($utente, "getInfo") ? $utente->getInfo() : []; |
|
// Prendiamo il CF dell'utente |
|
$cf = $userInfo["cf"] ?? null; |
|
if (!$cf) { |
|
$reason = ["code" => "USER"]; |
|
break; |
|
} |
|
|
|
$permission = $this->getPermissionFor($apiEndpoint, $method, $dynamicPermission); |
|
if ($apiEndpoint !== null && $method !== null) { |
|
// Verifichiamo i permessi se necessario |
|
$endpointConfig = $this->api_config[$apiEndpoint] ?? null; |
|
$methodConfig = $endpointConfig["methods"][$method] ?? null; |
|
} |
|
|
|
|
|
// NO_AUTH non fa verifiche |
|
if ($permission !== null && $permission !== DelegheRoles::NO_AUTH) { |
|
// Otteniamo il permesso dell'utente |
|
$userRole = $this->getUserRole($cf, $permission, RoleSyncBehaviour::NEVER_SYNC); |
|
// Permesso negato |
|
if ($userRole === null || !in_array($userRole, $permission)) { |
|
$permissionText = implode(", ", $permission); |
|
$txtRole = $userRole ?? "Nessuno"; |
|
if ($apiEndpoint !== null && $methodConfig !== null) { |
|
$methodFriendlyName = ucfirst(str_replace("-", " ", $methodConfig["path"] ?? "")); |
|
$apiFriendlyName = ucfirst(camelCaseToWords($apiEndpoint)); |
|
$msg_pre = "Per effettuare l'operazione <b>{$methodFriendlyName}</b> su <b>{$apiFriendlyName}</b>"; |
|
} else { |
|
$msg_pre = "Per usare questo modulo"; |
|
} |
|
$reason = [ |
|
"code" => "PERMISSION_DENIED", |
|
"msg" => "{$msg_pre} ti serve uno dei seguenti ruoli: <b>{$permissionText}</b>. Ruolo rilevato: <b>{$txtRole}</b>" |
|
]; |
|
|
|
return false; // Il messaggio viene popolato qui |
|
} |
|
// Se il permesso è dinamico, dobbiamo verificare la tipologia scheda |
|
if ($dynamicPermission && in_array($userRole, DelegheRoles::DRPs)) { |
|
// TODO Implementare verifica tipo scheda |
|
} |
|
} |
|
// Tutti i controlli passati |
|
$canDoRequest = true; |
|
} while (false); |
|
|
|
if (!empty($reason)) { |
|
$reason["msg"] = $this->managePermissionMessage($reason["code"]); |
|
} |
|
return $canDoRequest; |
|
} |
|
|
|
/** |
|
* managePermissionMessage |
|
* Mostra un messaggio amichevole relativo all'eventuale errore di autorizzazione |
|
* |
|
* @param mixed $code |
|
* @return void |
|
*/ |
|
public function managePermissionMessage(String $code) |
|
{ |
|
$return = "Errore sconosciuto"; |
|
if ($code == "RUP_RP_MISMATCH") { |
|
$rp_cf = $this->getUserFromRole(DelegheRoles::RP); |
|
if (isset($this->oggetto) && method_exists($this->oggetto, "getRup")) { |
|
$rup_cf = $this->oggetto->getRup()["codice_fiscale"] ?? ""; |
|
} else { |
|
$rup_cf = ""; |
|
} |
|
$return = "Il codice fiscale del Responsabile Unico di Procedura (<b>RUP</b>) <code class='text-dark'>{$rup_cf}</code> <i>(impostato sul pannello Incarichi)</i> |
|
non corrisponde al codice fiscale del Responsabile Procedura (<b>RP</b>) <code class='text-dark'>{$rp_cf}</code> <i>(impostato sui Permessi ANAC).</i> |
|
{{STEP}} |
|
<br>- Se invece si tratta di un errore, assicurarsi di impostare il Codice Fiscale corretto sul pannello Incarichi. |
|
<br><br> |
|
Il modulo ANAC resterà disattivo fino alla risoluzione dell'incongruenza."; |
|
|
|
if ($this->canClearDossier()) { |
|
$step = <<<ERR |
|
<br>- Se si intende cambiare RP, sarà necessario cliccare su |
|
<span class='badge badge-danger'><span class='fa fa-sync mr-2'></span>Aggiorna RP</span> |
|
per riallineare RUP e RP. |
|
ERR; |
|
} else { |
|
$step = <<<ERR |
|
<br>- Se si intende cambiare RP, il <b>nuovo incaricato</b> deve cliccare su |
|
<span class='badge badge-danger'><span class='fa fa-lock mr-2'></span>Permessi ANAC</span> > |
|
<span class='badge badge-warning'><span class='fa fa-user-tie mr-2'></span>Prendi carico</span> |
|
per diventare il nuovo Responsabile Procedura. |
|
ERR; |
|
} |
|
$return = str_replace("{{STEP}}", $step, $return); |
|
} else if ($code == "USER") { |
|
$return = "Codice fiscale dell'utente autenticato mancante, aggiorna il tuo profilo"; |
|
} else if ($code == "AUTH") { |
|
$return = '<a href="javascript:void(0)" onclick="showModalWithURL(`/backend/utenti/utility/spid.php`)">Livello di autenticazione non sufficiente</a>'; |
|
if (!empty($_SESSION["utente"]->authenticationLevel) && $_SESSION["utente"]->authenticationLevel >= 1) { |
|
if ($_SESSION["ente"]->certificazioneManualeUtenti()) { |
|
if ($_SESSION["utente"]->getInfo()["certificato"] == "S") { |
|
$return .= " - <a href=\"#\" onClick=\"showModalWithURL('/profilo/enable2FA.php');\">Abilita l'autenticazione a due fattori per procedere</a>"; |
|
} else { |
|
$return .= " - Rivolgiti ad un amministratore per certificare l'utenza o accedi tramite SPID o CIE per procedere"; |
|
} |
|
} else { |
|
$return .= " - Accedi tramite SPID o CIE per procedere"; |
|
} |
|
} else { |
|
$return .= " - Accedi tramite SPID o CIE per procedere"; |
|
} |
|
} |
|
return $return; |
|
} |
|
|
|
/** |
|
* Shorthand per getServiceStatus che ritorna una booleana |
|
* |
|
* @param boolean $forceRefresh |
|
* @return bool True se tutti i servizi sono funzionali |
|
*/ |
|
public function getServiceStatusBool(bool $forceRefresh = false) |
|
{ |
|
return $this->getServiceStatus($forceRefresh)["title"] === "OK"; |
|
} |
|
|
|
/** |
|
* Questo metodo verifica che i server anac siano live |
|
* |
|
* @param boolean $forceRefresh Se impostato viene forzatamente effettuata una nuova ricerca, altrimenti viene chiesto ogni 10 minuti |
|
* @return array Risposta del server o della cache |
|
*/ |
|
public function getServiceStatus(bool $forceRefresh = false) |
|
{ |
|
global $pdo; |
|
//return ["title" => "KO", "updated_at" => "1970-01-01", "created_at" => "1970-01-01"]; |
|
$tabella = "b_stato_servizio_npa"; |
|
// Peschiamo l'ultimo status |
|
$lastStatus = $pdo->go("SELECT * FROM {$tabella} ORDER BY codice DESC LIMIT 1")->fetch(PDO::FETCH_ASSOC); |
|
if (empty($lastStatus)) { |
|
// Fallback |
|
$lastStatus = [ |
|
"status" => 999, |
|
"title" => "KO", |
|
"detail" => "KO", |
|
"created_at" => "1970-01-01", |
|
"updated_at" => "1970-01-01" |
|
]; |
|
} |
|
|
|
|
|
// Per il cron forniamo sempre l'ultima risposta |
|
$isCron = defined("__CMDS_JOB_EXEC") && !empty(__CMDS_JOB_EXEC); |
|
if ($isCron) { |
|
$lastStatus["internal"] = "CRON"; |
|
return $lastStatus; |
|
} |
|
|
|
// Verifichiamo se dobbiamo forzare un refresh da API |
|
$shouldRefresh = $forceRefresh; |
|
if (!$shouldRefresh) { |
|
// Calcoliamo il tempo passato |
|
$timeDiff = time() - strtotime($lastStatus["updated_at"]); |
|
// Ricarichiamo ogni 10 minuti se l'ultimo stato era OK oppure ogni minuto se non lo era |
|
$shouldRefresh = $lastStatus["title"] === "OK" ? $timeDiff > 600 : $timeDiff > 60; |
|
} |
|
|
|
// Se non dobbiamo ricaricare, ritorniamo l'ultimo status |
|
if (!$shouldRefresh) { |
|
unset($lastStatus["codice"]); |
|
return $lastStatus; |
|
} |
|
// Se l'utente non è autorizzato evitiamo di effettuare la richiesta |
|
if (!$this->canDoRequest($cantRequest)) { |
|
$lastStatus["internal"] = "NO_PERMISSION"; |
|
return $lastStatus; |
|
} |
|
// Adesso passiamo alla richiesta |
|
$serviziComuni = $this->api_config["serviziComuni"]; |
|
// Se non abbiamo le impostazioni ritorniamo l'ultimo status |
|
if (empty($serviziComuni)) { |
|
$lastStatus["internal"] = "NO_SETTINGS"; |
|
return $lastStatus; |
|
} |
|
|
|
|
|
// Se l'utente non è autorizzato evitiamo di effettuare la richiesta |
|
if (!$this->canDoRequest($cantRequest, "serviziComuni", "serviceStatus")) { |
|
$lastStatus["internal"] = "NO_PERMISSION"; |
|
return $lastStatus; |
|
} |
|
|
|
$claims = $this->getAnacJwsCustomClaims($this->api_config["serviziComuni"]["endpoint"]); |
|
$result_str = $this->apiCall( |
|
"serviziComuni", |
|
"serviceStatus", |
|
[], |
|
$claims |
|
); |
|
|
|
// Verifichiamo la richiesta |
|
if (empty($result_str) || !valid_json($result_str, $newStatus, TRUE)) { |
|
|
|
// Questo workaround si assicura che una risposta di diniego per autorizzazione |
|
// mancante non venga considerata un outage. |
|
if (str_contains($result_str, "AuthorizationContentDeny")) { |
|
$lastStatus["internal"] = "UNAUTHORIZED"; |
|
return $lastStatus; |
|
} |
|
|
|
// Se è invalida, consideriamolo un outage |
|
$newStatus = [ |
|
"status" => "999", |
|
"title" => "KO", |
|
"detail" => "Il Server ANAC non ha risposto correttamente", |
|
]; |
|
}; |
|
|
|
// Ora che abbiamo un nuovo status |
|
$save = new salva(); |
|
$save->nome_tabella = $tabella; |
|
$save->operazione = "INSERT"; |
|
$save->expect = ["status", "title", "detail", "created_at", "updated_at"]; |
|
$save->debug = FALSE; |
|
|
|
|
|
if ($newStatus["title"] === $lastStatus["title"]) { |
|
// Se il nuovo stato è analogo a quello precedente ci limitiamo ad aggiornare updated_at |
|
$save->operazione = "UPDATE"; |
|
$save->expect[] = "codice"; |
|
$lastStatus["updated_at"] = date("Y-m-d H:i:s"); |
|
$save->oggetto = $lastStatus; |
|
$newStatus = $lastStatus; |
|
} else { |
|
// Altrimenti inseriamo il nuovo status |
|
$newStatus["created_at"] = date("Y-m-d H:i:s"); |
|
$newStatus["updated_at"] = date("Y-m-d H:i:s"); |
|
$save->oggetto = $newStatus; |
|
} |
|
$save->save(); |
|
|
|
unset($newStatus["codice"]); |
|
return $newStatus; |
|
} |
|
|
|
public static function manageANACResponse(string $result_str, ?string &$message = null): ?object |
|
{ |
|
if (empty($result_str)) { |
|
$message = <<<ERR |
|
<p>I server ANAC hanno fornito una risposta vuota.</p> |
|
<p class="text-left"> |
|
Possibili cause: |
|
<ul class='text-left'> |
|
<li>Il Codice Fiscale dell'utenza in uso non è associato ad un profilo AUSA abilitato per questa procedura.</li> |
|
<li>Alternativamente potrebbe indicare un problema tecnico dei server ANAC.</li> |
|
</ul> |
|
</p> |
|
ERR; |
|
return null; |
|
} |
|
if (!valid_json($result_str, $result)) { |
|
$message = __("I server ANAC hanno fornito una risposta in un formato JSON non valido. Riprova più tardi."); |
|
return null; |
|
} |
|
return $result; |
|
} |
|
|
|
} |
|
/** |
|
* Comportamenti di richiesta della delega |
|
*/ |
|
class Retries401 |
|
{ |
|
const NORMAL = 0; |
|
const UPPERCASE = 1; |
|
const LOWERCASE = 2; |
|
const MAX = self::LOWERCASE; |
|
}
|
|
|