Przejdź do głównej zawartości
getnextpdf.com

Enterprise edycja

SaaS — szczegółowa referencja

Moduł Enterprise SaaS dostarcza wielodostępne bloki konstrukcyjne dla usługi opartej na NextPDF.

  • TenantContext to niezmienny obiekt wartości tożsamości, rozwiązywany wyłącznie z uwierzytelnionego kontekstu.
  • ApiKeyGenerator i ApiKeyAuthenticator wydają i walidują klucze API z prefiksem, sumą kontrolną i przechowywaniem w postaci skrótu.
  • QuotaChecker bramkuje żądania względem limitów przypadających na najemcę: ostrzeżenie przy 80%, odrzucenie przy 100%, odmowa w trybie fail-closed, gdy zużycie jest nieznane.
  • SidecarJwtMinter wytwarza krótkotrwałe tokeny usługowe HS256 na potrzeby wywołań między komponentami.
  • UsageMeter i StripeMeteringSyncer pobierają zdarzenia zużycia i synchronizują je z dostawcą rozliczeń z deterministyczną idempotencją.

Ta możliwość jest dostarczana w NextPDF Enterprise (nextpdf/enterprise) i aktywuje się wraz z kopertą licencyjną poziomu Enterprise. Wdrożenie bez tego uprawnienia nie ładuje klas tej możliwości. Porównaj edycje i uzyskaj licencję.

Powierzchnia SaaS to podstawowa możliwość Enterprise; nie istnieje osobna flaga dla poszczególnych funkcji. NextPDF Core (Apache-2.0) i NextPDF Pro nie mają modelu wielodostępności, kluczy API ani limitów; ta możliwość nie ma odpowiednika na niższym poziomie.

Okno terminala
composer require nextpdf/enterprise:^3

Wszystkie symbole znajdują się w przestrzeni NextPDF\Enterprise\SaaS.

SymbolParametryZachowanie domyślneZwracaZgłasza lub kończy się błędemUwagi
TenantContextstring $tenantId, string $source, array $scopes = ['read']Niezmienny obiekt wartości tożsamościobiekt wartościNicŹródła: jwt, mtls, api_key; hasScope() / hasAnyScope() testują zakresy
TenantContext::singleTenant()brakStały najemca default z read, write, adminTenantContextNicWdrożenia jednonajemcowe
ApiKeyAuthenticator::authenticate()string $rawKeySześcioetapowa walidacja, następnie rozwiązanie kontekstuTenantContextApiKeyAuthenticationException (HTTP 401)source kontekstu to api_key; zakresy kopiowane z rekordu klucza
ApiKeyAuthenticator::requireScope()TenantContext $context, ApiKeyScope $requiredScopeJawne stwierdzenie zakresuvoidApiKeyAuthenticationException::insufficientScope() (HTTP 403)Egzekwowanie zakresu to osobny, jawny krok
ApiKeyGenerator::generateLive() / ::generateTest()brakNowy klucz: prefiks, 32-znakowy korpus base62 (entropia 192-bitowa), 4-znakowa suma kontrolnaarray{key, hash, prefix}NicPrefiksy npf_live_ / npf_test_; hash to skrót przechowywania
ApiKeyGenerator::validateChecksum()string $keyKontrola kształtu prefiksu, długości i sumy kontrolnej CRC32boolNicZabezpieczenie przed literówką przed jakimkolwiek wyszukaniem w magazynie danych; nie jest mechanizmem bezpieczeństwa
ApiKeyGenerator::hashKey() (statyczna)string $keySzesnastkowy skrót SHA-256 surowego kluczastringNicJedyna przechowywana reprezentacja klucza
ApiKeyGenerator::isLiveKey() / ::isTestKey()string $keySprawdzenie prefiksuboolNicŚrodowisko widoczne bez wyszukiwania
ApiKeyid, najemca, skrót klucza, wyświetlany prefiks, maska zakresów, momenty utworzenia/wygaśnięcia/unieważnieniaPrzechowywany rekord klucza; tekst jawny nigdy nie jest utrwalanyobiekt wartościNicisActive(), isRevoked(), isExpired(), scopeNames()
ApiKeyScopeenum z wartością: Read = 1, Write = 2, Admin = 4Model zakresów jako maska bitowaenumNicmaskFromNames(), fromName(), fullAccess(); nieznane nazwy są ignorowane przez konstruktor maski
ApiKeyRepositoryInterfaceKontrakt przechowywania; utrwalanie wyłącznie skrótuZależne od implementacjifindByHash(), findActiveByTenant(), store(), revoke()
SidecarJwtMinter::__construct()string $secret, issuer, audience, int $ttlSeconds = 300Odrzuca sekret podpisujący krótszy niż 16 bajtów podczas konstrukcjiinstancjaInvalidArgumentExceptionDolny próg siły klucza 128 bitów; zalecane 32 lub więcej losowych bajtów
SidecarJwtMinter::mint()TenantContext $tenantJWT HS256 z iss, aud, sub, scope, tenant_id, iat, exp, jtistringJsonException przy niepowodzeniu kodowania roszczeńDomyślny czas życia pięć minut; jti to 16 losowych bajtów zakodowanych szesnastkowo
QuotaChecker::check()TenantContext $tenant, TenantQuota $quotaOdczytuje bieżące zużycie; ostrzega przy 80%; odrzuca przy 100%; odmawia, gdy zużycie jest nieznanearray{allowed: bool, warning_percentage: float|null}QuotaExceededException, QuotaUnavailableExceptionWywołanie zwrotne alertu wywoływane przy obu progach
TenantQuotafloat $maxCuPerPeriod, kolekcje, bajty magazynu, równoległe zadaniaLimity na okres; stała progu miękkiego 80%obiekt wartościNicWartości domyślne fromConfig(): 10,000 CU, 100 kolekcji, 10 GB, 10 zadań
QuotaExceededException::toErrorEnvelope()brakKoperta błędu SPEC-QUOTA-001arrayHTTP 402, bez ponawiania; niesie bieżące zużycie, limit oraz moment resetu
QuotaUnavailableException::toErrorEnvelope()brakKoperta błędu SPEC-QUOTA-503arrayHTTP 503, z możliwością ponowienia; powód usage_undeterminable
UsageMeter::pullUsage()array<string, int> $watermarksOdpytuje każdy skonfigurowany host źródła zużycia od jego kursoraarray{events, instance_id}UsageMeterException, gdy każdy host jest nieosiągalnyCzęściowa awaria tolerowana; nieosiągalne hosty logowane i pomijane
UsageMeter::getCurrentUsage()string $tenantIdZużycie jednostek obliczeniowych w bieżącym okresiefloatUsageMeterException, gdy zużycie jest nieokreślalneDające się sparsować zero jest autorytatywne; nieznane zużycie zgłasza wyjątek
StripeMeteringSyncer::sync()array<string, int> $watermarksJeden cykl pobrania, transformacji i wysyłkiarray{watermarks, sent, failed}Nic; niepowodzenia wysyłki trafiają do wywołania zwrotnego DLQNiepowodzenie pobrania zwraca cykl bez działania zachowujący kursor
StripeAdapter::sendMeterEvent()MeterEvent $eventPOST do dostawcy z nagłówkiem idempotencjivoidStripeSyncExceptionHTTP 429 i 5xx z możliwością ponowienia; pozostałe 4xx bez ponawiania
StripeAdapter::sendBatch()list<MeterEvent> $eventsWysyła każde zdarzenie; zbiera niepowodzenialist<StripeSyncException>NicPusta lista oznacza, że każde zdarzenie się powiodło
MeterEventnazwa miernika, najemca, wartość, klucz idempotencji, znacznik czasuNiezmienny obiekt wartości zdarzenia pomiarowegoobiekt wartościNictoStripePayload() serializuje ładunek dostawcy
final readonly class ApiKeyAuthenticator
{
public function __construct(
private ApiKeyRepositoryInterface $repository,
private ApiKeyGenerator $generator,
private LoggerInterface $logger,
) {}
public function authenticate(string $rawKey): TenantContext {}
public function requireScope(TenantContext $context, ApiKeyScope $requiredScope): void {}
}
final class QuotaChecker
{
public function __construct(
private readonly UsageMeterInterface $usageMeter,
private readonly LoggerInterface $logger,
private readonly Closure $quotaAlertCallback,
) {}
/** @return array{allowed: bool, warning_percentage: float|null} */
public function check(TenantContext $tenant, TenantQuota $quota): array {}
}
interface UsageMeterInterface
{
/** @return array<string, mixed> */
public function pullUsage(array $watermarks): array;
public function getCurrentUsage(string $tenantId): float;
}
final class StripeMeteringSyncer
{
public function __construct(
private readonly UsageMeterInterface $usageMeter,
private readonly StripeAdapterInterface $stripeAdapter,
private readonly LoggerInterface $logger,
private readonly Closure $dlqCallback,
) {}
/** @return array{watermarks: array<string, int>, sent: int, failed: int} */
public function sync(array $watermarks): array {}
}
final readonly class SidecarJwtMinter
{
public function __construct(
private string $secret,
private string $issuer = 'nextpdf-enterprise',
private string $audience = 'nextpdf-spectrum',
private int $ttlSeconds = self::DEFAULT_TTL_SECONDS,
) {}
public function mint(TenantContext $tenant): string {}
}
  • Tożsamość najemcy. Kontekst najemcy jest niezmienny: identyfikator najemcy, źródło rozwiązania, zakresy. Tożsamość jest rozwiązywana wyłącznie z uwierzytelnionego kontekstu (jwt, mtls, api_key) — nigdy z nagłówka ani parametru zapytania dostarczonego przez klienta. Wdrożenie jednonajemcowe używa stałego kontekstu default z pełnymi zakresami.
  • Kolejność uwierzytelniania. Uwierzytelnianie kluczem API przebiega w ustalonej kolejności: suma kontrolna, skrót SHA-256, wyszukanie w repozytorium, kontrola unieważnienia, kontrola wygaśnięcia, rozwiązanie kontekstu. Klucze nieznane, unieważnione i wygasłe to trzy odrębne wyniki, wszystkie HTTP 401; niewystarczający zakres to HTTP 403.
  • Tajność klucza. Surowy klucz nigdy nie jest przechowywany ani logowany; utrwalany i wyszukiwany jest wyłącznie jego skrót SHA-256. Uwierzytelniacz sam nie wykonuje bajtowego porównania sekretu; wyszukanie skrótu w czasie stałym to kontrakt implementacji repozytorium.
  • Progi limitu. Przy limicie miękkim 80% żądanie przebiega dalej, zwracany jest procent ostrzeżenia i uruchamiane jest wywołanie zwrotne alertu. Przy limicie twardym 100% żądanie jest odrzucane z SPEC-QUOTA-001 (HTTP 402) niosącym moment resetu — pierwszy dzień następnego miesiąca, północ UTC.
  • Limit w trybie fail-closed. Nieokreślalne zużycie powoduje odmowę żądania z SPEC-QUOTA-503 (HTTP 503, z możliwością ponowienia). Nieznane zużycie nigdy nie jest traktowane jako zero. Rzeczywiste, dające się sparsować zerowe zużycie jest autorytatywne i dopuszcza żądanie.
  • Deduplikacja alertów. Kontroler nie deduplikuje alertów; deduplikacja w obrębie okresu należy do wywołania zwrotnego.
  • Synchronizacja pomiarów. Cykl jest zaplanowany, nigdy nie na ścieżce żądania. Wznawia od znaczników wodnych dla poszczególnych źródeł i przesuwa każdy kursor do najwyższej pomyślnie wysłanej tożsamości zdarzenia. Klucz idempotencji jest deterministyczny — najemca, okres, tożsamość zdarzenia — więc ponownie wysłane zdarzenie zostaje scalone przez deduplikację po stronie dostawcy.
  • Niepowodzenie pobrania. Nieudane pobranie zwraca cykl bez działania (sent 0, failed 0), który zachowuje znaczniki wodne; następny cykl ponawia to samo okno zamiast je pominąć.
  • Tokeny usługowe. Tokeny to HS256 ze współdzielonym sekretem i niosą iss, aud, sub, scope, tenant_id, iat, exp oraz unikalny jti. Domyślny czas życia to pięć minut. Konstrukcja odrzuca sekret krótszy niż 16 bajtów w trybie fail-closed.
  • Zniekształcony klucz nie przechodzi sumy kontrolnej i jest odrzucany przed jakimkolwiek dostępem do magazynu danych. Klucz poprawnie zbudowany, lecz nieznany, jest odrzucany po wyszukaniu. Oba ujawniają się jako wynik nieprawidłowego klucza.
  • Klucze nieznane, unieważnione i wygasłe używają odrębnych fabryk wyjątków; flaga keyExpired jest prawdziwa tylko w wyniku wygaśnięcia. Odwzoruj je na odrębne odpowiedzi dla klienta.
  • QuotaChecker::check() zwraca wartość tylko przy dopuszczeniu; zwrócone allowed jest zawsze true. Odrzucenie i niedostępność to wyniki wyjątkowe.
  • TenantQuota::usagePercentage() zwraca 0.0 dla niedodatniego limitu; fromConfig() podstawia wartości domyślne za brakujące i ogranicza limity całkowite do co najmniej 1.
  • Znaczniki wodne są dla poszczególnych źródeł; brakujący znacznik wodny startuje od początku strumienia danego źródła (kursor 0). Wdrożenie wieloźródłowe utrzymuje niezależne znaczniki wodne.
  • Transformacja pomija zdarzenia niebędące tablicą, zdarzenia z brakującą lub pustą operacją albo najemcą, niedodatnią wartością lub nieodwzorowaną operacją — bez powodowania niepowodzenia cyklu. Zdarzenie pozbawione użytecznej dodatniej całkowitej tożsamości jest odrzucane z ostrzeżeniem: losowy klucz zastępczy zniweczyłby deduplikację po stronie dostawcy i mógłby doprowadzić do podwójnego obciążenia najemcy.
  • Dziesięć kolejnych niepowodzeń wysyłki eskaluje do krytycznego wpisu w logu; licznik jest zerowany przy dowolnej udanej wysyłce. Każde nieudane zdarzenie i tak trafia do wywołania zwrotnego dead-letter.
  • Zniekształcony ładunek JSON z hosta źródła zużycia daje pustą listę zdarzeń, a nie niepowodzenie cyklu. pullUsage() zgłasza wyjątek tylko wtedy, gdy każdy skonfigurowany host jest nieosiągalny.
  • Prymitywy skrótu i MAC to SHA-256 oraz HMAC-SHA256 poprzez dostawcę kryptografii PHP hosta. Kompilacja ograniczona do FIPS kończy w trybie fail-closed na niezatwierdzonym algorytmie zamiast obniżać poziom; warstwa SaaS nie dodaje własnej polityki kryptograficznej.
  • Korpusy kluczy i identyfikatory tokenów pochodzą z CSPRNG (random_int(), random_bytes()).
  • Suma kontrolna CRC32 nie jest mechanizmem kryptograficznym i nie podlega trybowi FIPS.

Poniższe stwierdzenia opisują możliwość względem przywołanych klauzul. Nie są roszczeniami certyfikacyjnymi; NextPDF nie posiada certyfikacji dla tego modułu.

ZachowanieOdniesienie
Semantyka not-after exp tokenu usługowegoRFC 7519 §4.1.4
Kompaktowa serializacja JWS tokenu usługowegoRFC 7515 §3.1
Dolny próg 16-bajtowego sekretu HS256; brak haseł zapamiętywalnych przez człowieka jako kluczy MACRFC 8725 §3.5 (zagrożenie: §2.2)
Kontrakt wyszukania skrótu w repozytorium w czasie stałymOWASP ASVS 5.0 §11.2.4
Skrót przechowywania klucza API SHA-256FIPS 180-4 (zadeklarowane w kodzie)

Cytowania RFC 8725 oraz OWASP ASVS 5.0 są zweryfikowane przez RAG; pełne identyfikatory odniesień zapisano w bloku frontmatter tej strony. Odniesienia FIPS 180-4, FIPS 198-1 oraz BSI TR-02102-1 są zadeklarowane w kodzie źródłowym produktu (hash('sha256', …) oraz udokumentowany dolny próg klucza w minterze); nie zostały pobrane z korpusu RAG dla tej strony. Wymóg czasu stałego z ASVS §11.2.4 wiąże implementację repozytorium dostarczaną przez operatora, a nie samą klasę uwierzytelniacza.

  • Dostarcz trwałe implementacje ApiKeyRepositoryInterface oraz StripeAdapterInterface; pakiet dostarcza kontrakty i klienta dostawcy PSR-18, a nie warstwę utrwalania.
  • Zależności to wyłącznie abstrakcje PSR: logger PSR-3, klient HTTP PSR-18, fabryki żądań i strumieni PSR-17. Nie jest wymagany żaden SDK dostawcy.
  • Uruchamiaj synchronizację pomiarów jako zaplanowane zadanie. Utrwalaj zwrócone znaczniki wodne trwale po każdym cyklu.
  • Uwidocznij klientom procent ostrzeżenia limitu, na przykład jako nagłówek ostrzeżenia, i deduplikuj alerty limitu w obrębie okresu w wywołaniu zwrotnym.
  • Dostarcz sekret mintera tokenów z konfiguracji jako losową wartość o wysokiej entropii; zalecane 32 lub więcej losowych bajtów. Nigdy nie wyprowadzaj go z hasła.
  • Prefiksy kluczy czynią środowisko widocznym bez wyszukiwania; klucze piaskownicy i produkcyjne nigdy nie kolidują, ponieważ prefiks uczestniczy w przechowywanym skrócie.
  • Szczegóły wewnętrznego mechanizmu pozostają w wewnętrznej dokumentacji repozytorium źródłowego i są poza zakresem tego podręcznika.

Ta strona dokumentuje wyłącznie zewnętrznie obserwowalne zachowanie oraz wspieraną publiczną powierzchnię API. Wewnętrzne ścieżki przestrzeni nazw, klasy pomocnicze, tabele mechanizmów, nazwy plików runbooków oraz prefiksy zgłoszeń są poza zakresem.