Deklarovanie pečatí
Zapečatiteľný model implementuje Sealable, používa HasSeals a v statickej metóde defineSeals() deklaruje jednu či viac pomenovaných pečatí. Prvá pečať je predvolená: Sentinel::for($model) bez názvu mieri na ňu, kým isIntact() a verifyAll() pokrývajú všetky pečate.
use Illuminate\Database\Eloquent\Model;
use RoundlyConsulting\Sentinel\Concerns\HasSeals;
use RoundlyConsulting\Sentinel\Contracts\Sealable;
use RoundlyConsulting\Sentinel\Definition\SealBuilder;
use RoundlyConsulting\Sentinel\Enums\Algorithm;
use RoundlyConsulting\Sentinel\Enums\Reaction;
final class Invoice extends Model implements Sealable
{
use HasSeals;
public static function defineSeals(SealBuilder $seals): void
{
$seals->seal('financial')
->attributes('customer_id', 'currency', 'amount', 'status', 'paid', 'due_on')
->decimal('amount', 2)
->computed('lines', static fn (Invoice $invoice): array => $invoice->lines()
->orderBy('id')->get(['sku', 'quantity'])->toArray())
->algorithms(Algorithm::HmacSha256, Algorithm::Ed25519)
->scope(static fn (Invoice $invoice): string => (string) $invoice->tenant_id)
->verifyOnRetrieve(Reaction::Throw);
$seals->seal('identity')->using(PartyIdentitySeal::class)->lenient();
}
}Viac pečatí použite, keď majú časti riadku rôznych vlastníkov či životné cykly (finančné hodnoty oproti identite strany), rôzne kľúče alebo rôznu prísnosť. Každá sa ukladá ako jeden riadok sentinel_seals na (model, pečať) a má vlastnú verziu. Názvy pečatí zodpovedajú ^[a-z][a-z0-9_.-]{0,63}$ a v rámci modelu sú jedinečné.
Metódy buildera
| Metóda buildera | Účinok |
|---|---|
attributes(...$columns) | Pokryté stĺpce, typované podľa castov modelu. Nikdy *; primárny kľúč je viazaný vždy. |
string(), integer(), boolean(), decimal($column, $scale), float($column, $scale), datetime(), date(), json(), binary(), plaintext() | Pridajú stĺpce s explicitným typom. Float musí mať deklarovanú presnosť; plaintext() zapečatí dešifrovanú hodnotu castu encrypted. |
computed($name, $resolver, ?SealType $as) | Hodnota vypočítaná z modelu (statická closure), napr. súvisiace riadky. |
ring($ring), acceptRings(...$rings), algorithms(...$algorithms) | Kruh kľúčov, ďalšie kruhy prijímané počas migrácie kruhu, allowlist algoritmov. |
strict() / lenient() | Chýbajúca pečať je zistenie (predvolené) / je Unsealed. |
auto() / manual() | Pečatí sa pri Eloquent zápisoch (predvolené) / len explicitne. |
verifyOnRetrieve(?Reaction $reaction) | Overí každý načítaný model: Throw (model nenačíta) alebo Report; oba spustia TamperDetected a zalogujú. Null = verification.retrieve_reaction. |
fieldTags(bool $enabled = true) | Ukladá kľúčované značky polí, aby zlyhania pomenovali zmenené atribúty (kľúče HMAC). |
scope($resolver) | Scope tenanta (či iný) viazaný do MAC — pečať skopírovaná do iného scope sa nikdy neoverí. |
onTamperedWrite(TamperedWritePolicy $policy) | Prepis sealing.on_tampered_write pre jednu pečať. |
using(SealDefinition::class) | Najprv uplatní znovupoužiteľnú definíciu; inline volania majú prednosť. |
Polia sú explicitný allowlist zoradený v kanonickom dokumente podľa názvu: zo stĺpca je a:<column>, z vypočítanej hodnoty c:<name>. Typované metódy berú viac stĺpcov (->string('number', 'iban')), okrem decimal() a float(), ktoré berú jeden stĺpec a presnosť; typované volanie po attributes() prepíše odvodený typ daného stĺpca.
Znovupoužiteľné definície
use RoundlyConsulting\Sentinel\Contracts\SealDefinition;
use RoundlyConsulting\Sentinel\Definition\SealDefinitionBuilder;
final class PartyIdentitySeal implements SealDefinition
{
public function define(SealDefinitionBuilder $seal): void
{
$seal->attributes('number', 'meta')->plaintext('secret');
}
}Trieda sa z kontajnera vytvorí raz, pri kompilácii. Inline volania po using() pridávajú polia a prepisujú typy polí aj voľby.
Typy polí
SealType je deklarovaný typ poľa — $as v computed() a tag uložený v manifeste. Inštancie vystavujú kind, scale, tag() a equals() a SealType::tryFromTag('dec:2') tag spätne rozparsuje:
| Pomenovaný konštruktor | Tag |
|---|---|
SealType::string() | str |
SealType::integer() | int |
SealType::boolean() | bool |
SealType::decimal(int $scale) | dec:N (0–30) |
SealType::float(int $scale) | flt:N |
SealType::datetime() | dt |
SealType::date() | date |
SealType::json() | json |
SealType::binary() | bin |
SealType::plaintext() | plain |
SealType::auto() | auto |
use RoundlyConsulting\Sentinel\Definition\SealType;
$seals->seal('financial')
->decimal('amount', 2)
->computed('total', static fn (Invoice $invoice): string => $invoice->totalAsString(), SealType::decimal(2));Odvodenie typu z castov
attributes() určí typ každého stĺpca podľa castov modelu:
| Cast na modeli | Tag |
|---|---|
int, integer | int |
timestamp | auto — zapečatí sa uložená hodnota tak, ako je; pre normalizovaný tvar deklarujte datetime() |
bool, boolean | bool |
decimal:N | dec:N |
float, double, real | chyba kompilácie, ak nie je deklarovaný float('col', scale) |
string, string-backed enum | str |
int-backed enum | int |
date, immutable_date | date |
datetime, immutable_datetime, datetime:<fmt>, immutable_datetime:<fmt>, custom_datetime | dt |
array, json, object, collection, AsArrayObject, AsCollection | json |
AsStringable | str |
encrypted* | str (šifrový text tak, ako je uložený), ak nie je deklarované plaintext() |
custom CastsAttributes / no cast | auto (typuje sa za behu podľa surovej hodnoty; float sa odmietne) |
Hodnoty sa z databázy čítajú vždy v surovom tvare drivera — pri pečatení pod zámkom riadku — a normalizujú sa podľa tagu, takže riadok zapečatený na SQLite sa overí na PostgreSQL aj MySQL pri každom deklarovanom alebo castovanom type. Pole auto (bez castu a bez deklarovaného typu) sa typuje podľa PHP hodnoty, ktorú vráti driver, a tá sa líši pri booleanoch, necastovaných desatinných číslach a JSON texte: ak sa pečate majú presúvať medzi databázami, typ deklarujte.
Vypočítané hodnoty
Resolver vypočítanej hodnoty je statická closure, ktorá dostane model. Reťazec alebo Stringable sa stane str, int int, bool bool, backed enum svojou hodnotou, DateTimeInterface dt (UTC), pole alebo JsonSerializable json. Float potrebuje $as; inštancia modelu sa odmietne — vráťte kľúč alebo pole. Resolvery bežia nad modelom naplneným zo zamknutého riadku v databáze, nikdy nad inštanciou v pamäti volajúceho.
Vypočítané hodnoty zo súvisiacich riadkov zastarajú, keď sa tie riadky zmenia bez uloženia vlastníka. Vlastníka znovu zapečaťte — Sentinel::seal($invoice) prepečatí posun len vypočítaných hodnôt a zaznamená ho — alebo ho aktualizujte z hooku saved potomka ($line->invoice->touch()).
Kompilácia a chyby
Definícia sa skompiluje raz za proces, pri bootovaní triedy modelu — neplatná teda zlyhá už pri prvom new — a overí sa ako celok. defineSeals() nesmie čítať stav požiadavky ani autentifikácie a každá closure musí byť statická. Všetky problémy vypíše jedna InvalidSealDefinitionException (problems()):
- Žiadne pečate; duplicitný alebo neplatný názov pečate; pečať bez polí; duplicitné pole; neplatný názov stĺpca či vypočítanej hodnoty; uvedený primárny kľúč.
- Nedeklarovaný float; presnosť decimal mimo 0–30.
- Neznámy kruh; kruh, ktorý používajú podpisy HTTP správ, ako kruh pečate alebo v acceptRings; algorithms() mimo allowlistu kruhu; acceptRings obsahujúce vlastný kruh pečate.
- Model bez HasSeals; trieda v using(), ktorá nie je SealDefinition; nestatická closure.
Neznámy stĺpec sa prejaví ako QueryException databázy pri prvom zapečatení či overení (žiadny dopyt na schému pri každom volaní) — vopred to skontrolujete cez sentinel:verify --check-schema alebo sentinel:inspect --check-schema. Načítanie modelu nestojí nič navyše, pokiaľ niektorá z jeho pečatí nedeklaruje verifyOnRetrieve().
Kontrola definície
$definition = Sentinel::model(Invoice::class)->definition('financial'); // CompiledSeal
$definition->name; // 'financial'
$definition->ring;
$definition->manifest(); // [['a:amount', 'dec:2'], ['a:currency', 'str'], …, ['c:lines', 'auto']]
Sentinel::model(Invoice::class)->seals(); // ['financial', 'identity']
Sentinel::for($invoice)->definition(); // the handle's sealManifest (názvy polí a deklarované tagy) sa ukladá s každou pečaťou. Keď sa definícia zmení, no dáta sú neporušené, overenie hlási Outdated (predvolene neporušené) a sentinel:reseal --only-outdated presunie riadky na nový manifest.
Rezervované názvy
HasSeals pridá do modelu seal, verifySeal, verifySealOrFail, isIntact, acknowledgeTampering, persistSealed, sentinelSeals (vzťah MorphMany) a scopes whereSealed, whereNotSealed a withSeals a prepíše save(), delete(), chránenú incrementOrDecrement() a na Laraveli 13+ aj incrementOrDecrementEach().
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.