GNL 0.17: erken aşamada ve hızlı ilerliyor — API'ler sürümden sürüme değişebilirBize katıl

GNL
Dokümanlar menüsü
Enterprise@gnldev/auth-ee

Kullanıcı yönetimi (journal-destekli)

Bearer token → Principal doğrulayan, journal'da token'ı HASH'li saklayan kullanıcı deposu; Studio 'Kullanıcılar' görünümünden ekle/sil, token yalnız oluşturmada bir kez gösterilir.

Ne işe yarar / ne zaman kullanılır#

Açık-çekirdek roleAuth sabit kod içinde tanımlı birkaç token'la çalışır — yeni bir kullanıcı eklemek için deploy gerekir. createJournalUserStore bunun yerine kullanıcıları journal'da tutar (policy/budget'ta olduğu gibi veri-güdümlü): operatör Studio'dan yeni bir kullanıcı oluşturur, dönen token o kişiye verilir, kullanıcı o token'la Authorization: Bearer <token> ile istek atar — deploy'suz, kod değişikliği olmadan.

Somut senaryo: bir SaaS dağıtımında operatör (organizasyonsuz admin) her yeni müşteri organizasyonu için Studio "Kullanıcılar" görünümünden bir üye oluşturur (organizasyon ataması ile), token'ı müşteriye iletir; müşteri o token'la kendi organizasyonuna kapsanmış şekilde API'yi kullanır. Organizasyon-admin ise yalnız kendi organizasyonunun üyelerini yönetebilir.

Kurulum / import#

paket
import { createEnterpriseAuth, createJournalUserStore, createJournalAuditSink } from '@gnldev/auth-ee';
import { toJournal } from '@gnldev/durable';
import { SqliteStorage } from '@gnldev/durable/sqlite';

@gnldev/auth-ee paralı/lisanslı bir pakettir. createJournalUserStore, listKeys, putIfAbsent ve putIfMatch destekleyen bir journal ister — son ikisi eşzamanlı isteklerde koltuk sayısını ve e-posta tekilliğini doğru tutar; toJournal(storage) buna uyar (birinci taraf journal'ların hepsinde üçü de var). createPostgresUserStore(pool) aynı dizini kendi tablolarında tutar ve aynı şekilde cevap verir.

Adım adım kullanım#

1. Depoyu journal üzerinde kurun ve createEnterpriseAuth'a userStore olarak verin — SSO'dan sonra, açık-çekirdek fallback'ten önce devreye girer:

src/index.ts
const userStore = createJournalUserStore(toJournal(storage));
const auth = createEnterpriseAuth({
  licenseKey,
  failClosed: true,
  userStore,
  audit: createJournalAuditSink(toJournal(storage)),
  fallback: roleAuth({
    admin: { token: ADMIN_TOKEN, user: 'ops' },  // bootstrap operator
  }),
});

2. Aynı userStore'u Studio'ya users seçeneğiyle verin — bu, Studio'da "Kullanıcılar" görünümünü ve /users uçlarını açar:

src/index.ts
app.mount('/studio', createStudioApp({
  reader: toJournal(storage),
  gnl,
  auth,
  users: userStore, // the Studio "Users" view — add/remove; the token is shown once
  userErasure: { storage }, // DELETE /users/:id also erases the person's data; without it → 409
}));

3. Studio'dan (veya userStore.create() ile programatik) yeni kullanıcı oluşturunca dönen token yalnız o an gösterilir — sunucu düz token'ı hiçbir yerde saklamaz, yalnız SHA-256 hash'ini journal'a yazar. Kullanıcının id'si asla e-posta değildir: her kullanıcı rastgele bir id (usr_…) alır, e-posta ayrı ve tekil bir alandır. Koşumlar ve thread'ler sahip olarak bu id'yi kaydeder; aynı e-postayla gelen bir sonraki kişiye aynı id verilseydi önceki kişinin verisi ona geçerdi. Bundan önce yazılmış kayıtlar e-posta biçimli id'lerini korur:

programatik oluşturma
const { user, token } = await userStore.create({
  email: '[email protected]',
  roles: ['viewer'],
  orgId: 'acme',
  ttlMs: 30 * 24 * 60 * 60 * 1000, // expires automatically after 30 days
});
// token: 'eeu_...' — returned HERE ONLY; if it is lost, userStore.revoke() then create() again

4. Kullanıcı o token'ı Authorization: Bearer eeu_... başlığıyla gönderir; authenticate() token'ın SHA-256'sını journal'daki ters indekste arar, süresi dolmuş (expiresAt) veya iptal edilmiş (revoked) değilse bir Principal döner ve lastUsedAt'i best-effort günceller.

Kullanıcıyı silmeden erişimini kesmek için userStore.revoke(id) kullanılır — kayıt (audit/geçmiş için) kalır, yalnız token ters indeksi kaldırılır ve revoked: true işaretlenir. İptal edilen kullanıcı koltuğunu boşaltır.

Koltuk sayısı lisanstan gelir. createEnterpriseAuth, lisanstaki seats'i verdiğiniz userStore'a bağlar; depo bu sayıyı kendi verisine yazar (journal'da __eelicence__:seats, Postgres'te gnl_ee_licence tablosu). Aynı veri üzerindeki her depo bunu create'te uygular — Studio'ya verilen ikinci bir depo nesnesi ya da başka bir süreçte kurulan depo lisansı aşamaz. Koltuk atomik olarak alınan bir yuvadır; iki eşzamanlı istek son koltuğu birlikte alamaz. Sınır aşılınca create SeatsExceededError fırlatır. Deponun kendi seats seçeneği lisanstan düşük olabilir (kendi tavanınız); yüksek olanı boot'ta reddedilir.

Studio'da silmek, yalnız dizin kaydını değil kişiyi siler. DELETE /users/:id token'ı iptal eder, kişinin koşumlarını, thread'lerini, hafızasını ve dokümanlarını @gnldev/durable'ın eraseSubject'i ile siler, sonra kaydı kaldırır — bu yüzden Studio'ya userErasure verilmelidir (ajanlarınızın çalıştığı storage, isteğe bağlı erasers ve memory). Verilmezse silme 409 ile reddedilir: bunun yerine kullanıcıyı iptal edin ya da veriyi bilerek tutuyorsanız (yasal saklama, başka yerde yürüttüğünüz bir silme) ?keepData=true ile silin. Çalışmakta olan bir tur beklenir, en fazla userErasure.settleMs kadar (varsayılan 10 000 ms); süre dolduğunda hâlâ yazan koşumlar iptal edilir ve silme hiçbir şey silmeden 409 user_runs_in_flight döner — kullanıcı listede, iptal edilmiş olarak kalır; aynı silme tekrar gönderilince tamamlanır. Tek başına userStore.remove(id) yalnızca dizin kaydını (kayıt, token, koltuk, e-posta) kaldırır.

API referansı#

fncreateJournalUserStore

(journal: JournalLike, opts?: { seats? }) => JournalUserStore. journal listKeys, putIfAbsent ve putIfMatch desteklemeli, yoksa fırlatır. seats kendi tavanınızdır, lisanstakini aşamaz.

typeJournalUserStore

authenticate(token, now?), list(), create(input), update(id, patch), remove(id), revoke(id), seats, bindLicenceSeats(seats) sözleşmesi. create() { user, token } döner ve koltuk sınırında SeatsExceededError fırlatır; remove() yalnızca dizin kaydını siler.

typeEeUserRecord

Journal'da saklanan tam kayıt: id (usr_…), email?, name?, roles, kind?, orgId?, permissions?, tokenHash, createdAt, expiresAt?, lastUsedAt?, revoked?, seat?.

typeEeUserPublic

EeUserRecord eksi tokenHash ve seat; kind her zaman çözülmüş olarak (eksik kind subject okunur) — list() ve Studio'ya açılan güvenli görünüm.

typeCreateUserInput

create() girdisi: email?, name?, roles?, permissions?, orgId?, kind? (varsayılan subject), ttlMs? (öncelikli), expiresAt?.

constStudioAppOptions.users

createStudioApp'e verilen StudioUserStore — verilirse Studio 'Kullanıcılar' görünümü + /users uçları açılır.

constStudioAppOptions.userErasure

{ storage, erasers?, memory?, settleMs? } — Studio'nun DELETE /users/:id ucunun kişinin verisini kayıtla birlikte silmesini sağlar. Verilmezse silme 409 döner (?keepData=true hariç).

Dikkat
@gnldev/auth-ee paralı/lisanslıdır. Verilmiş ama geçersiz bir licenseKey varsayılan olarak boot'u durdurur; anahtar yoksa ya da failClosed: false ise createEnterpriseAuth fallback'e düşer ve userStore devreye girmez. features listesinde users olmayan bir lisans, userStore'u girişte uyarıyla yok sayar (failClosed: true ile boot hatası olur).
Not
Yetkilendirme varsayılan olarak tek eksenlidir: rol 'admin' (yazma) ya da 'viewer' (okuma) olur ve bir organizasyona kapsanır. Gözden kaçması kolay iki tane daha var — 'member' ikisinin arasında durur (okuma artı agents:run, yani biri admin olmadan ajan koşturabilir) ve ayrılmış 'platform-admin' organizasyonlar-arası açık yetkidir. Sonuncusu olmadan organizasyonsuz bir kullanıcı fail-closed'dır, yani kimse bir orgId unutarak süper admin olmaz. Ayrı bir permissions[] verdiğinizde rolün yetkilerini tümden ezer — altı adlandırılmış okuma izni kişi başına böyle atanır.
İpucu
Studio'da "Kullanıcılar" görünümü organizasyon modeline uyar: organizasyonsuz operatör tüm üyeleri yönetir, organizasyon-admin yalnız kendi organizasyonunun üyelerini oluşturabilir/silebilir/iptal edebilir — hedef üye var olmayan bir organizasyona atanamaz.