Overovanie
Každý vstup — fasáda, handle, trait modelu, middleware, validačné pravidlo, makro kolekcie, overenie pri načítaní aj skeny — vedie k tomu istému overovaču a tomu istému verdiktu:
use RoundlyConsulting\Sentinel\Enums\VerificationStatus;
use RoundlyConsulting\Sentinel\Facades\Sentinel;
$result = Sentinel::for($invoice)->verify(); // the default seal
$result = Sentinel::for($invoice, 'identity')->verify(); // a named seal
$result = Sentinel::verify($invoice, 'financial'); // the same, flat
$result->status; // VerificationStatus::Intact, ::Tampered, …
$result->reason; // 'mac', 'seal_deleted', 'newer_version', … (null when intact)
$result->changedAttributes; // ['a:amount'] — names only, never values
$result->changedColumns(); // ['amount'] (and changedComputed() for c:* fields)
$result->onlyComputedChanged(); // only computed (c:*) values drifted — an explicit seal() re-seals that
$result->toArray(); // status, reason, ids, key, changed names — never values
$result->isIntact();
$invoice->isIntact(); // every seal
$invoice->verifySeal('identity');
Sentinel::verifyAll($invoice)->allIntact(); // VerificationReport over every seal
Sentinel::verifyMany($invoices, 'financial')->failures();
Sentinel::for($invoice)->verifyOrFail(); // TamperedModelException when not intactAkcia berie VerifyRequest; checkLedger: false pre jedno volanie vynechá porovnanie s denníkom:
use RoundlyConsulting\Sentinel\Actions\Seals\VerifyModelAction;
use RoundlyConsulting\Sentinel\DataTransferObjects\VerifyRequest;
$result = app(VerifyModelAction::class)->execute(new VerifyRequest($invoice, 'financial', checkLedger: false));Výsledok
VerificationResult nesie status, reason, typ, id a pečať modelu, version a ledgerVersion, ring, keyId, algorithm, sealedAt, changedAttributes, context (VerificationContext) a outdatedIsIntact. isIntact() je true pre Intact, Unsealed a — pri predvolenom verification.outdated_is_intact — aj Outdated; failed() je jeho negácia a toArray() nikdy neobsahuje hodnoty. verifyAll() a verifyMany() vracajú VerificationReport s results, allIntact(), failures(), count(), counts() a throwIfTampered().
Stavy
| Stav | Význam |
|---|---|
Intact | Pečať zodpovedá uloženým hodnotám aj denníku. |
Outdated | Neporušená, ale zapečatená podľa staršej definície (predvolene sa počíta ako neporušená; sentinel:reseal --only-outdated). |
Unsealed | Benevolentná pečať, ktorá sa nikdy nezapísala. |
Tampered | Hodnoty sa zmenili mimo aplikácie (mac), posunuli sa len vypočítané hodnoty (computed — zápisy a seal() ju znovu zapečatia), hodnoty sa už nedajú kanonizovať (canonicalization) alebo bol sfalšovaný záznam denníka (ledger_entry). |
Missing | Striktná pečať chýba: seal_deleted (história existuje), never_sealed, unsealed. |
Stale | Obnovila sa staršia pečať: newer_version, not_in_ledger, ledger_mismatch (entity_recreated pri znovu použitom id). |
UnknownKey, RevokedKey, RetiredKey | Pečatiaci kľúč je neznámy/čakajúci/poškodený, odvolaný alebo vyradený. |
AlgorithmNotAllowed, AlgorithmMismatch | Algoritmus kľúča nie je povolený / líši sa od uloženého. |
Malformed | Riadok pečate je poškodený (neznámy formát, nedekódovateľný manifest, neplatné kódovanie MAC). |
Unverifiable | V riadku chýba zapečatený stĺpec (missing_attribute), uložená pečať pokrýva vypočítané pole, ktoré definícia už nedeklaruje (missing_computed), alebo overenie riadku pri skene vyhodilo výnimku (error). |
Každé zlyhanie spustí TamperDetected (synchrónne) a zaloguje varovanie do verification.log_channel — len názvy, nikdy hodnoty. Overovanie len číta: zámky berie iba v rámci kontroly pred zápisom.
Poradie stavov
Vyhodnocuje sa v poradí a vyhráva prvá zhoda. Stavy 6–11 sa rozhodnú ešte pred výpočtom MAC a riadky 15–18 sa vykonajú pri zapnutom verification.check_ledger (jeden indexovaný dopyt):
| # | Podmienka | Stav | Dôvod |
|---|---|---|---|
| 1 | Model nie je sealable / pečať nie je deklarovaná | vyhodí SealingMisconfiguredException | — |
| 2 | Žiadny riadok pečate; hlava denníka je živá | Missing | seal_deleted |
| 2a | Žiadny riadok pečate; hlava denníka je tombstone s neplatným MAC | Tampered | ledger_entry |
| 3 | Žiadny riadok pečate; striktná | Missing | unsealed / never_sealed |
| 4 | Žiadny riadok pečate; benevolentná | Unsealed | — |
| 5 | Neznámy formát, nedekódovateľný manifest, stĺpec manifestu, ktorý nepatrí pečati ani tabuľke, MAC nie je base64url, verzia < 1 | Malformed | príslušná kontrola |
| 6 | Uložený kruh nie je kruh pečate ani v acceptRings | UnknownKey | ring_not_accepted |
| 7 | Kľúč nenájdený / čakajúci / zlyhala integrita obálky | UnknownKey | not_found / pending / integrity |
| 8 | Kľúč odvolaný | RevokedKey | — |
| 9 | Kľúč vyradený | RetiredKey | — |
| 10 | Algoritmus kľúča nepovoľuje pečať ani kruh | AlgorithmNotAllowed | — |
| 11 | Uložený algoritmus ≠ algoritmus kľúča | AlgorithmMismatch | — |
| 12 | Zapečatený stĺpec chýba v načítaní / vypočítané pole už nie je deklarované | Unverifiable | missing_attribute / missing_computed |
| 13 | Aktuálne hodnoty sa nedajú kanonizovať | Tampered | canonicalization |
| 14 | MAC / podpis neplatný, no MAC atribútov dokazuje posun len vypočítaných hodnôt | Tampered | computed |
| 14a | MAC / podpis neplatný inak | Tampered | mac |
| 15 | Denník: existuje záznam novší než riadok pečate | Stale | newer_version |
| 16 | Denník: žiadny záznam vo verzii riadku pečate | Stale | not_in_ledger |
| 17 | Denník: MAC pečate v zázname ≠ MAC riadku | Stale | ledger_mismatch |
| 18 | Denník: MAC záznamu neplatný alebo podpísaný kruhom, ktorý pečať neprijíma | Tampered | ledger_entry |
| 19 | Uložený manifest ≠ aktuálna definícia | Outdated | — |
| 20 | Inak | Intact | — |
Zmenené atribúty
S kľúčom HMAC a zapnutými značkami polí (predvolené) ukladá každá pečať kľúčované 128-bitové značky pre každé pole. Pri neplatnom MAC sú zmenenými atribútmi polia, ktorých prepočítaná značka sa líši, plus polia prítomné len na jednej strane. Značky slúžia len na diagnostiku: ich úprava iba zavádza zoznam — pečať aj tak zlyhá. Asymetrické pečate značky neukladajú, preto je changedAttributes null.
Overenie pri načítaní
Pečať deklarovaná s ->verifyOnRetrieve(Reaction::Throw) overí každý načítaný model: Throw zistenie nahlási (TamperDetected a riadok v logu) a vyhodí TamperedModelException; Report nahlási a model nechá načítať. Bez explicitnej reakcie platí verification.retrieve_reaction. Kontrola denníka je pri načítaní vypnutá, pokiaľ nezapnete verification.retrieve_checks_ledger, a čiastočný select() bez vlastných stĺpcov pečate sa preskočí. Sentinel::withoutVerification() overenie pozastaví a Sentinel::model(Invoice::class)->find($id) zámerne načíta zmenený riadok (pozri Potvrdzovanie zmien).
Skeny
Sentinel::model($class)->scan() overí všetky riadky po dávkach vrátane soft-deleted. Closure where (alebo builder daného modelu) zúži sken jedného modelu bez načítania do pamäte — len filtruje, nikdy nemení poradie — a callback progress dostane po každej dávke počet spracovaných riadkov:
$report = Sentinel::model(Invoice::class)->scan('financial', chunk: 500);
$report->scanned; // rows × seals verified
$report->counts; // list<StatusCount>
$report->findings; // up to maxFindings VerificationResult
$report->truncated;
$report->hasFindings(); // any failure
$report->hasFindings(VerificationStatus::Tampered, VerificationStatus::Missing); // only these
$report->count(VerificationStatus::Outdated);
$report = Sentinel::model(Invoice::class)->scan(where: fn ($query) => $query->where('tenant_id', 7));
$report = Sentinel::model(Invoice::class)->scan('financial', where: Invoice::query()->whereYear('created_at', 2026), limit: 10_000);Všeobecný tvar je Sentinel::scan(new ScanOptions(models: […], seal: null, chunk: 500, checkLedger: true, limit: null, maxFindings: 1000, checkSchema: false, where: null, progress: null)) a sentinel:verify ho obaľuje.
Prejavte lásku k open source
Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.
Ďalšie spôsoby podpory vrátane kryptomienOdoslaním daru súhlasíte s našimi podmienkami prijímania darov.
Chcete to zabudovať do svojho produktu?
Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.