store

package
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 4, 2026 License: Apache-2.0 Imports: 21 Imported by: 0

Documentation

Index

Constants

View Source
const (
	StateActive   = "active"
	StateInactive = "inactive"
	StateDeleted  = "deleted"
)

Hesap durumları.

View Source
const (
	PendingWaiting  = "waiting"
	PendingRejected = "rejected"
)

Pending durumları.

View Source
const (
	FileHistoryDefaultLimit = 200
	FileHistoryMaxLimit     = 1000
)

FileHistoryDefaultLimit / FileHistoryMaxLimit, geçmiş sorgusunun sınırları.

⚠️ DIŞARI AÇIKLAR çünkü kesilmiş bir listeyi "hepsi bu" diye göstermemek çağıranın işi: HTTP ucu, kaç satır istediğini bilmeden sonucun kırpılıp kırpılmadığını söyleyemez. Sınırı burada tutup orada bir kez daha yazsaydık, ikisi sessizce ayrışabilirdi.

Variables

View Source
var (
	// ErrNotFound, aranan kaydın olmadığını söyler.
	ErrNotFound = errors.New("store: not found")

	// ErrConflict, benzersizlik kısıtının ihlal edildiğini söyler
	// (aynı adla ikinci bir kullanıcı, rol, hedef...).
	ErrConflict = errors.New("store: already exists")

	// ErrInvalid: değerin kendisi kabul edilebilir değil — çağıranın
	// yeniden denemesi değil, DÜZELTMESİ gerekiyor. ErrConflict'ten
	// ayrı, çünkü "zaten var" ile "böyle olamaz" farklı cevaplar
	// gerektiriyor (409'a karşı 400).
	ErrInvalid = errors.New("store: invalid value")

	// ErrAccessDenied: kimlik geçerli ama postern'de karşılığı yok —
	// JIT sağlamada hiçbir grup role eşleşmediğinde.
	ErrAccessDenied = errors.New("store: access denied")

	// ErrIdentityConflict: kullanıcı adı var olan bir hesapla eşleşiyor
	// ama o hesap BAŞKA bir IdP kimliğine bağlı.
	//
	// ErrAccessDenied'dan AYRI TUTULUYOR çünkü ikisi operatöre bambaşka
	// şeyler söyler. "Grup eşlenmemiş" bir yapılandırma eksiğidir; bu
	// ise kullanıcı adı geri dönüşümü ya da hesap devralma denemesidir
	// — incelenmesi gereken bir olay. Tek bir sentinel altında
	// toplandığında giriş reddi "no mapped groups" diye loglanıyordu ve
	// olayı araştıran yönetici, düzeltmeyecek bir eşlemeyi düzeltmeye
	// gönderiliyordu.
	//
	// İstemciye giden yanıt İKİSİNDE DE aynı: ayrım yalnızca logda.
	ErrIdentityConflict = errors.New("store: identity conflict")

	/*
	 * ErrAdminBindRefused: adı eşleşen bir YÖNETİCİ hesabını, ilk kez
	 * görülen bir kimliğe bağlama denemesi.
	 *
	 * ErrIdentityConflict'ten AYRI, çünkü operatörün yapacağı şey
	 * farklı: orada bir devralma denemesi var ve incelenmeli; burada
	 * meşru bir yönetici de olabilir ve yolu açık — yöneticiliği
	 * geçici olarak kaldırıp bağlaması yeterli. Aynı sentinel altında
	 * toplansaydı, ikisi de "incelenecek olay" diye loglanır ve
	 * gerçekten incelenmesi gereken seyrek olay gürültüye karışırdı.
	 */
	ErrAdminBindRefused = errors.New("store: administrator account cannot be claimed by username")

	/*
	 * ErrAccountNotProvisioned: kimlik doğrulandı, postern hesabı yok ve
	 * hesapların kendiliğinden açılması KAPALI.
	 *
	 * ⚠️ ErrAccessDenied'dan AYRI, çünkü çağıranın yapacağı şey farklı:
	 * orada kapı kapanıyor, burada kişi onay kuyruğuna yazılıyor ve
	 * bunu ekranda görüyor. Tek sentinel altında toplansalardı kuyruk
	 * hiç dolmazdı.
	 */
	ErrAccountNotProvisioned = errors.New("store: account does not exist and auto-create is off")

	/*
	 * ErrAdminPasswordRefused: yönetici hesabına kullanıcı parolası
	 * konmak istendi.
	 *
	 * ⚠️ KURALI VERİTABANI TUTUYOR (göç 026), bu sentinel yalnızca onun
	 * reddini konuşulabilir bir hataya çeviriyor. Burada bir `if`
	 * yazmıyoruz: kural bir kez, kaçınılmaz olduğu yerde duruyor.
	 *
	 * Neden kural: acil durum kapısı tahmin edilebilir bir değere
	 * bağlanamaz. Yönetici hesabının kimlik bilgisi makine üretimi
	 * kalmak zorunda — postern'in "her şey bozulduğunda içeri girilir"
	 * iddiası tam olarak buna dayanıyor.
	 */
	ErrAdminPasswordRefused = errors.New("store: an administrator account cannot hold a password")
)
View Source
var ErrCredentialExists = errors.New("store: account already has a local credential")

ErrCredentialExists, hesabın zaten bir yerel kimlik bilgisi olduğunu söyler.

View Source
var ErrTooSlow = errors.New("store: search took too long and was stopped")

ErrTooSlow, arama sunucu tarafı zaman sınırına takıldı.

ErrInvalid DEĞİL: istek biçimsel olarak geçerliydi. Çağıranın söylemesi gereken şey "isteğin hatalı" değil, "bu arama fazla geniş".

Functions

func CleanSearchPath

func CleanSearchPath(p string) string

* CleanSearchPath, ağaç aramasında kullanılacak yolu normalleştirir. * * ⚠️ DIŞARI AÇIK ÇÜNKÜ UÇ, ARANANI YANKILIYOR. Panel sonuçta * "under /etc/" yazıp sorgunun "/etc" ile çalıştığını gizleseydi, * denetçi neyin arandığını yanlış bilirdi — ve "moved here" rozetini * çizen istemci karşılaştırması da normalleştirilmemiş yola bakıp * kayardı. * * Sondaki eğik çizgiler atılıyor; hepsi eğik çizgiyse kök dizin.

func ValidateLabel

func ValidateLabel(key, value string) error

ValidateLabel, anahtar/değer çiftini denetler.

DIŞA AÇIK, çünkü hedef yaratma yolu etiketleri hedefi AÇMADAN ÖNCE doğrulamak zorunda: önce açıp sonra etiketi reddetmek, yarısı uygulanmış bir istek bırakırdı.

Types

type AdminHolder

type AdminHolder struct {
	Username string `json:"username"`
	// Via: "cli", "group" ya da boş. Boş olan, admin_via sütunu
	// eklenmeden önce yönetici yapılmış eski bir kayıttır (017 göçü
	// mevcutları 'cli' sayıyor, yani pratikte boş kalmaması gerekir).
	Via string `json:"via"`
}

AdminHolder, yönetici bayrağını taşıyan kişi ve bayrağın KAYNAĞI.

Kaynağı da taşıması şart: panel "kim yönetici" sorusuna cevap verirken "bunu kim verdi" sorusunu da cevaplamak zorunda. Aksi hâlde grup üzerinden gelen yetki ile acil durum için elle açılmış hesap ekranda ayırt edilemez ve operatör, kaldıramayacağı bir yetkiyi kaldırabileceğini sanır.

type AdminLogEntry

type AdminLogEntry struct {
	At      time.Time
	Actor   string // web: oturum sahibi; cli: işletim sistemi kullanıcısı
	Via     string // "web" | "cli"
	Action  string // makine-okur: "user.create", "role.grant" ...
	Entity  string // etkilenen varlık adı
	Details string
}

AdminLogEntry, tek bir yönetici işleminin kaydı.

type ArchiveBacklogReport

type ArchiveBacklogReport struct {
	Pending int
	Oldest  time.Time
	Failing int

	/*
	 * Lost, dosyası KAYIP olduğu için hiç yüklenemeyecek kayıtların
	 * sayısı (session_archives.permanent).
	 *
	 * ⚠️ Pending'DEN AYRI. Bunları "bekliyor" saymak, hiç bitmeyecek
	 * bir kuyruk ve sönmeyen bir "disk dolacak" alarmı demekti — oysa
	 * bu satırlar için yapılacak bir şey yok, yalnızca GÖRÜLMELERİ
	 * gerekiyor. "Kayıp" ile "bekliyor" operatöre bambaşka şey söyler.
	 */
	Lost int
}

* ArchiveBacklogReport, yükleme kuyruğunun durumu. * * ⚠️ Failing AYRI SAYILIYOR. "Bekleyen 40" ile "bekleyen 40, 40'ı da * üst üste başarısız" farklı iki durum: birincisi yükleyicinin geride * kalması, ikincisi hiç ilerlemediği. Arşivleyici hatanın kalıcı mı * geçici mi olduğunu zaten hesaplıyordu ama sonucu yalnızca log * cümlesini seçmek için kullanıyordu — operatörün baktığı ekranda bu * ayrım hiç görünmüyordu. * * Eşik ÜÇ: bir geçici kesinti bir iki denemeyi düşürür; üç üst üste * başarısızlık artık "bir şeyi düzeltmen gerekiyor" demektir.

type ArchivePending

type ArchivePending struct {
	SessionID     string
	RecordingPath string
	StartedAt     time.Time
	Attempts      int
}

ArchivePending, yüklenmeyi bekleyen bir kayıt.

type ArchiveState

type ArchiveState struct {
	Archived   bool
	ArchivedAt time.Time
	Bucket     string
	ObjectKey  string
	SHA256     string
	SizeBytes  int64
	Attempts   int
	LastError  string
}

ArchiveState, bir oturumun arşiv durumu.

type Credential

type Credential struct {
	Verifier string
	// Chosen: değeri KULLANICI seçti (parola). Değilse makine üretimi
	// sır (015'in dünyası, acil durum kapısı).
	Chosen bool
	// MustChange: bir sonraki girişte değiştirilmek zorunda.
	MustChange bool
}

* Credential, hesabın yerel kimlik bilgisi. Yoksa ErrNotFound. * * ⚠️ ARTIK DÜZ BİR DİZE DEĞİL. Eskiden yalnızca doğrulayıcı dönüyordu * ve doğruydu: tek bir kimlik bilgisi türü vardı. İki tür olduğu an * çağıranın HANGİ türle karşılaştığını bilmesi şart — yanlış doğrulama * yolunu seçmek, ya kullanıcıyı dışarıda bırakır ya da biçim kontrolünü * atlar. Türü çağıranın tahmin etmesine bırakmak yerine tek sorguda * veriyoruz.

type DirectoryAccount

type DirectoryAccount struct {
	Username string
	Subject  string
	Groups   []string
}

DirectoryAccount, dizinden kendiliğinden açılacak hesabın girdisi.

type FileQuery

type FileQuery struct {
	// Path, aranan yol. Under false ise TAM eşleşme.
	Path string

	/*
	 * Under: Path bir DİZİN, altındaki her şey aransın.
	 *
	 * ⚠️ SORUŞTURMANIN EN SIK SORDUĞU BU. "/etc altında ne oldu"
	 * sorusunu tam eşleşmeyle sormak imkânsız; her dosyayı tek tek
	 * bilmek gerekirdi.
	 */
	Under bool

	// User / Target, olayın oturumundan süzme.
	//
	// ⚠️ Path'siz de anlamlılar: "ayse ne aldı" ve "web01'de ne oldu"
	// soruşturmanın ikinci ve üçüncü sorusu.
	User   string
	Target string

	Limit int
}

* FileQuery, dosya geçmişi aramasının ölçütleri. * * ⚠️ EN AZ BİRİ DOLU OLMALI. Üçü birden boşken "her şeyi göster" * demek, sorulmamış bir soruya dolu bir ekranla cevap vermek olurdu — * bu ekranın kaçındığı şeyin ta kendisi.

type FileTouch

type FileTouch struct {
	SessionFile
	/*
	 * Birleştirme LEFT JOIN, dolayısıyla bu alanlar boş kalabilir.
	 *
	 * ⚠️ BUGÜN BOŞ KALAMIYORLAR: 027'deki yabancı anahtar
	 * (ON DELETE RESTRICT) oturum satırının silinmesini engelliyor,
	 * yani her dosya olayının oturumu yerinde. LEFT JOIN yine de
	 * seçildi çünkü bedeli sıfır ve kısıt bir gün gevşetilirse doğru
	 * davranışı zaten yapıyor: olayın kendisi kanıt, yanındaki
	 * üstveriyi okuyamamak onu gizlemenin gerekçesi değil. Panel de
	 * bu ihtimale göre çiziyor (boş kullanıcıyı satırı yutarak değil,
	 * işaretleyerek gösteriyor).
	 */
	User   string `json:"user"`
	Target string `json:"target"`
	OSUser string `json:"os_user"`
	SrcIP  string `json:"src_ip"`
}

* FileTouch, bir dosya olayı ve onu YAPAN oturumun kimliği. * * ⚠️ OTURUM KİMLİĞİ TEK BAŞINA CEVAP DEĞİL. "/etc/shadow'u kim aldı" * sorusunun cevabı bir UUID değil, bir kişidir; olayları çıplak * session_id'lerle döndürmek, denetçiyi her satır için ayrı bir * sorguya mecbur bırakırdı.

type GroupMapping

type GroupMapping struct {
	ExternalGroup string
	Role          string
	CreatedAt     time.Time
	CreatedBy     string
}

GroupMapping, bir dış grubun hangi role karşılık geldiği.

type LocalCredentialHolder

type LocalCredentialHolder struct {
	Username   string
	IsAdmin    bool
	CreatedAt  time.Time
	CreatedBy  string
	LastUsedAt time.Time

	// Chosen: değeri kullanıcı seçti (parola), makine üretmedi.
	Chosen bool
	// MustChange: ilk girişte değiştirilmek zorunda, yani henüz
	// kullanılmamış bir "veren de biliyor" değeri.
	MustChange bool

	/*
	 * ⚠️ State BURADA OLMAK ZORUNDA.
	 *
	 * Ölçülen tutarsızlık: iki ayrı kilitlenme koruması (panelde
	 * canSwitchTo, CLI'da settings set) "yerel kimlik bilgisi olan bir
	 * yönetici var" gördüğünde paneli kapatmaya izin veriyor. Ama
	 * locallogin.go silinmiş hesabı REDDEDİYOR. Yani silinmiş bir
	 * yönetici, iki korumayı da tatmin edip kimsenin giremediği bir
	 * panel bırakabiliyordu. Durumu taşımayan bir liste, o soruyu
	 * cevaplayamaz.
	 */
	State string
}

LocalCredentialHolder, yerel kimlik bilgisi olan bir hesabın özeti.

type PendingUser

type PendingUser struct {
	ID      string `json:"id"`
	Subject string `json:"subject"`
	Source  string `json:"source"`

	// ⚠️ Username, Email ve SeenGroups YALNIZCA GÖSTERİM. Hiçbir karar
	// bunlara bakmıyor: üçü de kaynakta değişebiliyor.
	Username   string   `json:"username"`
	Email      string   `json:"email"`
	SeenGroups []string `json:"seen_groups"`

	State     string    `json:"state"`
	FirstSeen time.Time `json:"first_seen"`
	LastSeen  time.Time `json:"last_seen"`

	DecidedBy string    `json:"decided_by,omitempty"`
	DecidedAt time.Time `json:"decided_at,omitzero"`
	Reason    string    `json:"reason,omitempty"`
}

PendingUser, onay bekleyen (ya da reddedilmiş) bir kimlik.

type ProvisionRequest

type ProvisionRequest struct {
	// Username, IdP'nin verdiği kullanıcı adı — aynı zamanda hedeflerdeki
	// hesap adı olacak (os_user). Kurumsal ortamda "isim.soyisim".
	Username string
	Email    string
	// Groups, IdP'nin bildirdiği ham grup adları.
	Groups []string

	/*
	 * GroupsResolved, grupların GERÇEKTEN öğrenilip öğrenilmediği.
	 *
	 * ⚠️ SIFIR DEĞERİ false VE BU KASITLI. false iken SSO rollerine
	 * HİÇ DOKUNULMAZ. Alanı doldurmayı unutan bir çağrı yolu, rolleri
	 * TAZELEMEMİŞ olur — rolleri SİLMİŞ değil. İki yanlıştan geri
	 * dönülebilir olanı bu.
	 *
	 * Neden gerekti: kaynak "kullanıcıyı bulamadım" dediğinde de boş
	 * bir grup listesi geliyordu ve buradaki kod onu "hiçbir gruba üye
	 * değil" diye okuyup bütün SSO rollerini siliyordu. Giriş yolunda
	 * ne bekleme süresi ne tavan var; senkronizasyon döngüsündeki
	 * korumaların hiçbiri burada çalışmıyor.
	 */
	GroupsResolved bool

	// Issuer ve Subject, IdP kimliğinin KALICI anahtarı (OIDC "iss" ve
	// "sub"). Username DEĞİL bunlar eşleştirme anahtarıdır: username
	// birçok sağlayıcıda değiştirilebilir ve geri dönüştürülebilir.
	// Boş bırakılırsa eşleştirme yapılamaz ve ProvisionUser reddeder.
	Issuer  string
	Subject string

	/*
	 * AdminGroupMember, GELEN KİMLİĞİN kendisinin yönetici grubunda
	 * olduğu.
	 *
	 * ⚠️ Adı bir yöneticiyle eşleşen hesabı devralmanın önündeki kapıyı
	 * bu açıyor — ve açması doğru: yönetici grubunda olan kişi zaten
	 * yöneticidir, başka bir yönetici hesabını almakla YENİ bir yetki
	 * kazanmaz. Kapatsaydık, dizin grubundan yönetici olan herkes
	 * yükseltmeden sonra kendi hesabına giremezdi.
	 *
	 * ⚠️ Çağıran bunu YALNIZCA grupları gerçekten çözülmüşken true
	 * yapmalı. Sıfır değeri false ve bu kasıtlı: alanı doldurmayı
	 * unutan bir yol, kapıyı AÇMIŞ değil KAPATMIŞ olur.
	 */
	AdminGroupMember bool

	/*
	 * AutoCreate, hesabın KENDİLİĞİNDEN açılabileceği.
	 *
	 * ⚠️ Sıfır değeri false ve bu kasıtlı: alanı doldurmayı unutan bir
	 * çağrı yolu hesap AÇMAZ, açar değil. İki yanlıştan geri
	 * dönülebilir olanı bu.
	 *
	 * Kapalıyken kullanıcı ErrAccountNotProvisioned alıyor ve çağıran
	 * onu onay kuyruğuna yazıyor — kapıda bırakmıyor.
	 */
	AutoCreate bool
}

ProvisionRequest, JIT sağlama için kimlik sağlayıcıdan gelen bilgi.

type PublicKey

type PublicKey struct {
	Blob    []byte
	Comment string
	AddedAt time.Time
}

type PurgeResult

type PurgeResult struct {
	FormerUsername string
	Keys           int
	Roles          int
	At             time.Time
}

PurgeResult, purge'ün neyi serbest bıraktığı.

type PurgedAccount

type PurgedAccount struct {
	FormerUsername string
	PurgedAt       time.Time
}

PurgedAccount, purge edilmiş bir kaydın izi.

type SessionFile

type SessionFile struct {
	ID        string    `json:"id"`
	SessionID string    `json:"session_id"`
	At        time.Time `json:"at"`
	Op        string    `json:"op"`
	Path      string    `json:"path"`
	NewPath   string    `json:"new_path,omitempty"`
	Flags     string    `json:"flags,omitempty"`
	Read      int64     `json:"read"`
	Wrote     int64     `json:"wrote"`
	OK        bool      `json:"ok"`
	Detail    string    `json:"detail,omitempty"`
}

SessionFile, bir oturumda gerçekleşen tek bir dosya olayı.

type SessionStart

type SessionStart struct {
	ID string

	Username   string
	TargetName string

	OSUser string

	SrcIP         string
	StartedAt     time.Time
	RecordingPath string
}

type SettingView

type SettingView struct {
	Key       string
	Value     string // şifreliyse maskeli
	Secret    bool
	UpdatedAt time.Time
	UpdatedBy string
}

SettingView, listeleme için ayar görünümü.

type StaleAccount

type StaleAccount struct {
	Username    string
	State       string
	Confirmed   time.Time
	SSOOnly     bool
	DirBound    bool
	ManualRoles int
}

StaleAccount, süresi dolmuş bir hesabın özeti.

type Store

type Store struct {
	// contains filtered or unexported fields
}

func Open

func Open(ctx context.Context, conn string) (*Store, error)

func (*Store) AccountID

func (s *Store) AccountID(ctx context.Context, username string) (string, error)

* AccountID, adı verilen hesabın kimliğini (users.id) döner. * * ⚠️ Panel oturumu bu KİMLİĞE bağlanıyor, ada değil. Ad yeniden * kullanılabiliyor (purge onu serbest bırakıyor); kimlik kalıcı. Oturum * ada bağlı kalırsa, adı devralan yeni kişiye çözülüyor.

func (*Store) AccountState

func (s *Store) AccountState(ctx context.Context, username string) (state string, confirmed time.Time, err error)

AccountState, hesabın durumu ve son doğrulanma anı.

func (*Store) AddGroupMapping

func (s *Store) AddGroupMapping(ctx context.Context, externalGroup, roleName, actor string) error

AddGroupMapping, dış grubu role bağlar. Rol yoksa ErrNotFound; aynı eşleme ikinci kez eklenirse ErrConflict.

func (*Store) AddLocalCredential

func (s *Store) AddLocalCredential(ctx context.Context, username, verifier, by string) error

* AddLocalCredential, hesaba yerel kimlik bilgisi bağlar. * * ⚠️ ÜSTÜNE YAZMAZ. Var olan bir kimlik bilgisini sessizce * değiştirmek, komutu ikinci kez çalıştıran operatörün ELİNDEKİ sırrı * geçersiz kılardı — üstelik o an ekranda yeni bir sır gördüğü için * bunu fark etmeden. Daha kötüsü: kurulumun tek yöneticisinin kimlik * bilgisini, yazma yetkisi olan herkes böylece döndürebilirdi. * Değiştirmek istiyorsa açık bir komut kullanır.

func (*Store) AddPublicKey

func (s *Store) AddPublicKey(ctx context.Context, username string, keyBlob []byte, comment string) error

func (*Store) AddSessionFiles

func (s *Store) AddSessionFiles(ctx context.Context, sessionID string, files []SessionFile) error

* AddSessionFiles, bir grup olayı TEK transaction'da yazar. * * ⚠️ Toplu yazım şart: rsync gibi bir istemci binlerce küçük dosyaya * dokunur ve olay başına ayrı gidiş-dönüş, denetimi transferin hız * sınırı hâline getirirdi. Denetimi yavaşlatmak, onu kapatma isteği * doğurur. * * Ya hepsi yazılır ya hiçbiri: yarım yazılmış bir grup, "dosya açıldı * ama hiç kapanmadı" gibi görünen uydurma bir denetim satırı bırakırdı.

func (*Store) AdminLog

func (s *Store) AdminLog(ctx context.Context, limit int) ([]AdminLogEntry, error)

AdminLog, defteri YENİDEN ESKİYE döner. limit<=0 sınırsız.

func (*Store) AdminVia

func (s *Store) AdminVia(ctx context.Context, username string) (string, error)

AdminVia, yönetici yetkisinin kaynağını döner: "cli", "group" ya da boş (yönetici değil).

func (*Store) Admins

func (s *Store) Admins(ctx context.Context) ([]AdminHolder, error)

Admins, yönetici bayrağı taşıyan herkesi kaynağıyla döner.

func (*Store) AllTargetFacts

func (s *Store) AllTargetFacts(ctx context.Context) (map[string]model.TargetFacts, error)

AllTargetFacts, hedef adı → gözlemler.

TEK SORGU: hedef başına ayrı sorgu, ana ekranı çizerken kullanıcının erişebildiği her hedef için bir gidiş dönüş demekti (N+1). Etiketlerde aynı gerekçeyle allTargetLabels var.

func (*Store) AllowIdentityBind

func (s *Store) AllowIdentityBind(ctx context.Context, username string, at time.Time) error

AllowIdentityBind, bir yönetici hesabının SIRADAKİ kimlik bağlamasına izin verir. Tek kullanımlık.

func (*Store) ApplyAdminGroup

func (s *Store) ApplyAdminGroup(ctx context.Context, members []string) (granted, revoked []string, err error)

* ApplyAdminGroup, grup üzerinden yönetici kümesini TEK İŞLEMDE eşitler. * * ⚠️ NEDEN "bir sonraki girişte" DEĞİL, ŞİMDİ: * * Yetki eskiden yalnızca kişi giriş yaptığında güncelleniyordu. Bu, * yönetici grubu DEĞİŞTİĞİNDE sessiz bir sızıntı bırakıyor: eski gruptan * gelen kişi bir daha hiç giriş yapmasa da yönetici KALIYOR. "Grubu * değiştirdim" ile "yetki değişti" arasındaki fark, kimsenin bakmadığı * bir yerde süresiz açık duruyordu. * * Ayrıca onay ekranını dürüst yapan şey bu: ekran "bu kişiler yönetici * oluyor" diyorsa, kaydettikten sonra gerçekten öyle olmalı — "herkes * bir dahaki girişinde" değil. * * members'ın DB'de karşılığı olmayanları ATLANIR (hata değil): dizinde * var ama postern'e hiç girmemiş kişinin hesabı yok. İlk girişinde * applyGroupAdmin onu zaten yakalar. * * CLI'ın verdiği yöneticiliğe DOKUNMAZ — ne verirken ne alırken.

func (*Store) ApprovePending

func (s *Store) ApprovePending(ctx context.Context, id, osUser, by string) (PendingUser, error)

* ApprovePending, hesabı AÇAR ve kimliği bağlar. * * ⚠️ ROL VERMİYOR. Roller kişinin bir sonraki girişinde CANLI kaynaktan * çözülüyor; kuyruktaki grup listesi onay ekranında ne görüldüğünü * gösteriyor, bir yetki kaydı değil. Bayat bir fotoğrafa göre rol * yazmak, yetkiyi geçmişe bağlamak olurdu. * * Hesap ve bağ AYNI transaction'da: arada kalan bir hata, kimliği * bağlanmamış bir hesap bırakır ve o hesap sonra ADLA devralınabilir * hâle gelirdi.

func (*Store) ArchiveBacklog

func (s *Store) ArchiveBacklog(ctx context.Context) (b ArchiveBacklogReport, err error)

ArchiveBacklog, bekleyen kayıt sayısı ve EN ESKİSİNİN yaşı.

⚠️ YAŞ, SAYIDAN DAHA ÖNEMLİ. Ölmüş bir yükleyicinin belirtisi "sayı artıyor" değil, "en eskisi yaşlanıyor": sabit bir sayı da hiçbir şeyin ilerlemediği anlamına gelebilir.

func (*Store) ArchiveStateOf

func (s *Store) ArchiveStateOf(ctx context.Context, sessionID string) (ArchiveState, bool, error)

ArchiveStateOf, panelin gösterdiği durum.

func (*Store) ArchivedIDs

func (s *Store) ArchivedIDs(ctx context.Context, ids []string) (map[string]bool, error)

* ArchivedIDs, verilen oturumlardan HANGİLERİNİN doğrulanmış şekilde * arşivlendiğini döner. * * ⚠️ BUDAYICININ KAPISI BU ve varsayılan REDDETME: dönen kümede * OLMAYAN her kimlik "silme" demek. Sorgu hata verirse çağıran koşuyu * iptal ediyor — hiçbir şey silmeden. "Çözemedim" ile "güvende" aynı * şey değil ve burada karıştırmanın bedeli kanıtın yok olması.

func (*Store) AssignRole

func (s *Store) AssignRole(ctx context.Context, username, roleName string, expiresAt time.Time) error

AssignRole, kullanıcıya ELLE rol verir (source='manual').

expiresAt sıfır ise süresiz. Zaten verilmiş bir rolü tekrar vermek hata değil — ama artık tam olarak no-op da değil: expires_at GÜNCELLENİR, çünkü "bu yetkiyi uzat" doğal bir istek ve ayrı bir komut gerektirmesi için sebep yok.

Kullanıcı ya da rol yoksa ErrNotFound.

func (*Store) BeginTOTP

func (s *Store) BeginTOTP(ctx context.Context, username, secret string) error

* BeginTOTP, kayıt başlatır (ya da BAŞTAN başlatır). * * ⚠️ Doğrulanmış bir kaydın üzerine YAZMIYOR. Yazsaydı, oturumu çalınan * bir hesapta saldırgan kaydı sıfırlayıp kendi telefonunu bağlar ve * ikinci faktör sessizce el değiştirirdi. Doğrulanmış kaydı değiştirmek * ancak önce onu kapatmakla (DisableTOTP, kod ister) mümkün.

func (*Store) BindDirIdentity

func (s *Store) BindDirIdentity(ctx context.Context, username, subject string) error

* BindDirIdentity, bir postern hesabını bir dizin kimliğine bağlar. * * ⚠️ YALNIZCA HENÜZ BAĞLI DEĞİLSE (`dir_subject IS NULL`). Var olan bir * bağı sessizce değiştirmek, tam olarak önlenmeye çalışılan devralmayı * geri getirirdi — 011'in BindIdPSubject'teki aynı kuralı. * * Zaten başka bir kimliğe bağlıysa ErrConflict; başka bir HESAP o * kimliği tutuyorsa benzersiz indeks ihlali yine ErrConflict'e çevrilir. * İki durum da "buradan devam etme" demek.

func (*Store) BindIdPSubject

func (s *Store) BindIdPSubject(ctx context.Context, username, issuer, subject string) error

BindIdPSubject, bir postern hesabını bir IdP kimliğine bağlar.

SADECE HENÜZ BAĞLI DEĞİLSE: WHERE koşulu idp_subject IS NULL. Var olan bir bağı sessizce değiştirmek, tam olarak önlemeye çalıştığımız devralmayı geri getirirdi. Zaten bağlıysa ErrConflict.

func (*Store) ClaimArchives

func (s *Store) ClaimArchives(ctx context.Context, limit int, now time.Time,
	claimTimeout, retryAfter time.Duration) ([]ArchivePending, error)

* ClaimArchives, yüklenecek işleri üstlenir. * * ⚠️ ÜSTLENME ZAMAN AŞIMIYLA SERBEST KALIYOR, temizlikle değil. * Öldürülen bir süreç hiçbir şey temizleyemez; claimed_at'i eski olan * satırı yeniden üstlenilebilir saymak, yeniden başlatmadan sonra işin * kendiliğinden devam etmesinin tek yolu. * * ⚠️ GERİ ÇEKİLME last_attempt_at ÜZERİNDEN: az önce denenmiş ve * başarısız olmuş bir satırı hemen yeniden almak, kesinti sırasında * depoyu ve log'u boğardı. * * ⚠️ GERİ ÇEKİLME ÜSTEL — VE SABİTTİ. Her başarısız satır her turda * yeniden alınıyordu; arşivleyici hatanın kalıcı mı geçici mi olduğunu * HESAPLIYOR (objstore.ErrTransient) ama sonucu yalnızca log cümlesini * seçmek için kullanıyordu. Yanlış yazılmış bir kova adı ya da * reddedilen bir imza, düzeltilene kadar her turda yeniden denenip * log'u dolduruyordu — ve içindeki gerçek, geçici arızayı görünmez * yapıyordu. * * attempts ile katlanıyor, altıncıda duruyor: retryAfter=1dk ile * 1, 2, 4, 8, 16, 32, 64 dakika. Kalıcı olarak "vazgeçildi" diye * işaretlemiyoruz ve bu bilinçli — operatör kovayı düzelttiğinde * yeni bir komut çalıştırmak zorunda kalmasın; sıra kendiliğinden * ilerlesin.

func (*Store) ClaimByVerifiedEmail

func (s *Store) ClaimByVerifiedEmail(ctx context.Context,
	email, issuer, subject string, adminGroupMember bool) (model.User, error)

* ClaimByVerifiedEmail, giriş yollarının E-POSTA dalı için: hesabı * bulur ve aynı kapıdan geçirir. * * ⚠️ ProvisionUser KULLANILAMAZ, çünkü o gruplar çözülemediğinde * REDDEDİYOR — ve haklı: yeni bir hesap açıp açmamaya karar verecek * bilgi yok. Ama burada hesap ZATEN VAR; verilecek karar "bu kimlik bu * hesabı alabilir mi", ve o karar gruplara bağlı değil.

func (*Store) Close

func (s *Store) Close() error

func (*Store) CloseOrphanSessions

func (s *Store) CloseOrphanSessions(ctx context.Context, at time.Time) (closed, queued int64, err error)

* CloseOrphanSessions, açılışta sahipsiz kalmış oturum satırlarını kapatır. * * ⚠️ ÖLÇÜLEN ARIZA: postern SIGKILL alırsa (çökme, güç kesintisi, * `kill -9`) açık oturumların ended_at'i SONSUZA DEK NULL kalıyor. * Ölçüldü: bir oturum açıkken süreç öldürülüp yeniden başlatıldıktan * sonra satır hâlâ açıktı. Panel onu süresiz "çalışıyor" gösteriyor. * Kesme düğmesi bunu pasif bir görüntü hatasından aktif bir yalana * çeviriyordu: yönetici var olmayan bir oturumu kesmeye çalışırdı. * * ⚠️ YENİ BAŞLAYAN SÜREÇ HİÇBİR OTURUMA SAHİP DEĞİLDİR — kuralın * dayandığı tek önerme bu ve TEK DÜĞÜM varsayımıyla geçerli. İkinci bir * postern aynı veritabanına bağlıysa bu çağrı ONUN canlı oturumlarını * kapatır. 1.0 tek düğüm olduğu için kabul ediliyor; belgelerde * "bilinen sınırlar" altında yazılı. * * ⚠️ SATIR SİLİNMİYOR, KAPATILIYOR. Denetim izi kalmalı: "ne zaman * bittiğini bilmiyoruz" ile "hiç olmadı" ayrı şeyler. ended_at * açılış anı — gerçek bitişten sonra, yani süre OLDUĞUNDAN UZUN * görünebilir; bu yüzden çağıran kaç satır kapandığını LOG'A yazmalı.

func (*Store) ConfirmAccount

func (s *Store) ConfirmAccount(ctx context.Context, username string, at time.Time) error

* ConfirmAccount, kaynağın bu kişiyi ŞU AN doğruladığını kaydeder. * * ⚠️ HER BAŞARILI GİRİŞTE çağrılmalı — OIDC, dizin, hepsi. Zaman * temelli iptalin tek girdisi bu damga; koyulmayan bir kapı, o kapıdan * girenleri yavaş yavaş pasifleştirirdi. * * ⚠️ VE HESABI GERİ AKTİF EDİYOR. Pasifleşme "kaynak bunu bir süredir * doğrulamadı" demek; kaynak yeniden doğruladığı anda sebep ortadan * kalkıyor. Elle müdahale gerektirseydi, tatilden dönen herkes * yöneticiye başvururdu. * * 'deleted' hesap KENDİLİĞİNDEN geri gelmiyor: orada bir insan kararı * var (ya da uzun bir sessizlik) ve onu bir girişin sessizce bozması * doğru olmaz.

func (*Store) ConfirmTOTP

func (s *Store) ConfirmTOTP(ctx context.Context, username string, step int64) error

* ConfirmTOTP, kaydı DOĞRULANMIŞ işaretler. * * Yalnızca doğrulanmamış bir kayıt onaylanabilir: ikinci bir onay * çağrısı, kullanılmış bir adımı geri açmamalı.

func (*Store) CreateFromDirectory

func (s *Store) CreateFromDirectory(ctx context.Context, acc DirectoryAccount) (model.User, error)

* CreateFromDirectory, dizin kimliğinden hesap açar ve BAĞLAR. * * ⚠️ ROL EŞLEMESİ KAPIDA. Hiçbir grup bir role eşleşmiyorsa hesap * AÇILMIYOR (ErrAccessDenied). Bu, OIDC yolundaki ProvisionUser'ın * aynı sözleşmesi: "IdP'de hesabın olması postern'de hesabın olması * demek değil" kuralının otomatik açılıştaki karşılığı. Onsuz users * tablosu, hiçbir yere erişemeyen kayıtlarla dizinin kopyasına * dönüşürdü. * * ⚠️ Hesap ve bağ AYNI transaction'da: bağlanmamış bir hesap, adla * devralınabilir bir hesaptır.

func (*Store) CreateRole

func (s *Store) CreateRole(ctx context.Context, name string) (string, error)

func (*Store) CreateTarget

func (s *Store) CreateTarget(ctx context.Context, t model.Target) (string, error)

func (*Store) CreateUser

func (s *Store) CreateUser(ctx context.Context, username, email, osUser string) (string, error)

func (*Store) DeleteRole

func (s *Store) DeleteRole(ctx context.Context, name string) error

DeleteRole: rolün bağları (user_roles, role_targets) CASCADE ile gider; sessions rolü referanslamadığı için 787 bu yoldan çıkmaz — kontrol yine de duruyor, şema yarın değişirse sessizce yanlış hataya düşmeyelim.

func (*Store) DeleteSetting

func (s *Store) DeleteSetting(ctx context.Context, key string) error

Setting, tek bir ayarı döner; şifreliyse çözer. Yoksa ErrNotFound.

Anahtar yapılandırılmamışken şifreli bir ayara dokunmak AÇIK hata verir: boş string dönmek sırrı silinmiş gibi gösterir ve LDAP'ın parolasız bağlanmaya çalışmasına yol açardı. DeleteSetting, bir ayarı siler. Yoksa ErrNotFound.

Sır düşürmek için var: LDAP adresi değişince saklanan bind parolası düşürülüyor (bkz. httpapi.adminSetSetting). Değeri boşa çekmek yerine SATIRI silmek gerekiyor — boş bir değer "ayarlanmış ama boş" demek ve LoadConfig onu farklı yorumluyor.

func (*Store) DeleteTarget

func (s *Store) DeleteTarget(ctx context.Context, name string) error

DeleteTarget, hedefi ve rol bağlarını (CASCADE) kaldırır. Yoksa ErrNotFound; oturum kaydı varsa ErrConflict — denetim kaydı olan varlık silinmez, bu bir kısıt değil özellik (ayrıntı: isFKRestrict).

func (*Store) DeleteTargetLabel

func (s *Store) DeleteTargetLabel(ctx context.Context, targetName, key, actor, via string) error

DeleteTargetLabel, etiketi kaldırır. Yoksa ErrNotFound.

func (*Store) DeleteUser

func (s *Store) DeleteUser(ctx context.Context, username string) error

DeleteUser: kullanıcıyı, bağlarını ve anahtarlarını kaldırır. Oturum kaydı olan kullanıcı için ErrConflict. Erişimi kesmenin doğru yolu zaten silmek değil: anahtarlarını ve rollerini almak — kayıt kalır, kapı kapanır.

func (*Store) DirSubjectOf

func (s *Store) DirSubjectOf(ctx context.Context, username string) (string, error)

DirSubjectOf, hesabın bağlı olduğu dizin kimliğini döner; bağlı değilse boş dize.

func (*Store) DisableTOTP

func (s *Store) DisableTOTP(ctx context.Context, username string) error

DisableTOTP, ikinci faktörü kaldırır.

func (*Store) EndSession

func (s *Store) EndSession(ctx context.Context, id string, endedAt time.Time) error

func (*Store) FileHistory

func (s *Store) FileHistory(ctx context.Context, q FileQuery) ([]FileTouch, error)

* FileHistory, ölçütlere uyan dosya olaylarını en yeniden eskiye döner. * * Soruşturmanın sorusu bu: "/etc/shadow'u kim aldı". Oturumdan dosyaya * bakan bir arayüz bu soruyu cevaplayamaz — soruşturma dosyayı bilir, * oturumu bilmez. * * ⚠️ HEDEF YOLA DA BAKIYOR. Bir dosya bir yere rename ile gelmiş * olabilir ve o satırda aranan yol `path`te değil `new_path`te durur. * Yalnızca `path`e bakan bir arama, "/tmp/exfil buraya nereden geldi" * sorusuna "hiç dokunulmamış" cevabını verirdi — dosyayı oraya taşıyan * satır elimizdeyken. * * ⚠️ BOŞ ÖLÇÜT REDDEDİLİYOR — VE SEBEBİ ÖLÇÜLDÜ. Yalnızca yol * korumasını kaldırıp denedik: sorgu boş bir yol için BOŞ LİSTE * dönüyor (`new_path <> ”` koşulu, satırların çoğundaki boş * new_path'in eşleşmesini engelliyor). Yani tehlike rastgele bir liste * değil, boş bir liste: hiç sorulmamış bir soru "bu dosyaya * dokunulmamış" diye cevaplanmış görünürdü. * * ⚠️ ZAMAN SINIRLI ÇALIŞIYOR (searchtimeout.go). Ölçütler dışarıdan * geliyor ve havuzu SSH girişleri paylaşıyor.

func (*Store) FinishSyncRun

func (s *Store) FinishSyncRun(ctx context.Context, run SyncRun) error

FinishSyncRun, koşuyu sonuçlandırır.

Satır 'failed' olarak AÇILIYOR ve burada güncelleniyor: süreç ortasında ölürse kayıt "başarılı" değil "başarısız" kalır — sessizce başarılı görünen bir yarım koşu, olmayan bir senkronizasyona güvenilmesi demek olurdu.

func (*Store) FirstKeyAdded

func (s *Store) FirstKeyAdded(ctx context.Context, username string) (bool, error)

* FirstKeyAdded, hesabın ilk anahtarını EKLEMİŞ olup olmadığı. * * ⚠️ SAYIYA DEĞİL DAMGAYA BAKIYOR. "Şu an anahtarı var mı" diye sorsaydık * sil-ve-ekle kuralı tamamen atlardı: oturumu ele geçiren kişi mevcut * anahtarı siler, sayaç sıfırlanır, yeni anahtarı bedavaya ekler. Damga * bir kez konuyor ve bir daha kalkmıyor.

func (*Store) ForgetPending

func (s *Store) ForgetPending(ctx context.Context, id string) error

ForgetPending, satırı tamamen siler — reddi geri almanın yolu.

Var olması şart: yanlışlıkla reddedilen biri, aksi hâlde bir daha hiç başvuramazdı.

func (*Store) GrantTarget

func (s *Store) GrantTarget(ctx context.Context, roleName, targetName string) error

func (*Store) GroupMappings

func (s *Store) GroupMappings(ctx context.Context) ([]GroupMapping, error)

GroupMappings, tüm eşlemeleri grup adına göre sıralı döner.

func (*Store) HasIdPIdentity

func (s *Store) HasIdPIdentity(ctx context.Context, username string) (bool, error)

HasIdPIdentity, hesabın bir OIDC kimliğine bağlı olup olmadığı.

hasIdPIdentity'nin dışa açık eşi: sihirbaz "bu yönetici kaynağı çevirince kendini kilitler mi" sorusunu buradan soruyor.

func (*Store) ListPending

func (s *Store) ListPending(ctx context.Context) ([]PendingUser, error)

ListPending, kuyruğu döner (bekleyenler önce, en yeni başta).

func (*Store) LocalCredential

func (s *Store) LocalCredential(ctx context.Context, username string) (Credential, error)

LocalCredential, hesabın kimlik bilgisini döner. Yoksa ErrNotFound.

func (*Store) LocalCredentialHolders

func (s *Store) LocalCredentialHolders(ctx context.Context) ([]LocalCredentialHolder, error)

* LocalCredentialHolders, yerel kimlik bilgisi olan hesapları döner. * * ⚠️ GÖRÜNÜRLÜK BİR GÜVENLİK ÖZELLİĞİ. Bu hesaplar bir acil durum * kapısı ve kullanılmayan bir acil durum kapısı unutulmuş bir kapıdır. * Operatörün "kimlerde var, en son ne zaman kullanıldı" sorusunu * veritabanına girmeden cevaplayabilmesi gerekiyor.

func (*Store) LogAdmin

func (s *Store) LogAdmin(ctx context.Context, e AdminLogEntry) error

LogAdmin, bir yönetici işlemini deftere yazar (sıfır At damgalanır).

Çağıran, işlemi BAŞARIYLA bitirdikten sonra çağırır — başarısız denemeler HTTP log'unda zaten var; defter, "ne değişti"nin kaydı.

func (*Store) MarkArchiveFailed

func (s *Store) MarkArchiveFailed(ctx context.Context, sessionID, reason string, permanent bool, at time.Time) error

MarkArchiveFailed, denemeyi ve sebebini kaydeder.

archived_at'e DOKUNMUYOR: bir kez doğrulanmış kayıt, sonradan gelen bir hata yüzünden "güvende değil"e dönmemeli.

⚠️ permanent: bu kayıt hiç yüklenemeyecek (ör. dosyası yok). true ise satır candidate kümesinden çıkıyor; yoksa her turda yeniden claim edilip kuyruğu ve "disk dolacak" alarmını sonsuza dek meşgul ederdi.

func (*Store) MarkArchived

func (s *Store) MarkArchived(ctx context.Context, sessionID, bucket, key, sha string,
	size int64, at time.Time) error

* MarkArchived, yüklemenin DOĞRULANDIĞINI kaydeder. * * ⚠️ SIRA: yükle → deponun kendisine sor → BURAYA yaz → budayıcıya * silme izni doğ. Bu satır atıldığı andan itibaren kayıt yerelden * silinebilir hâle geliyor, dolayısıyla "gönderdim" ile "orada" * arasındaki farkı burada kapatmak zorundayız.

func (*Store) MarkDirectoryMissing

func (s *Store) MarkDirectoryMissing(ctx context.Context, username string, at time.Time) error

MarkDirectoryMissing, kullanıcının bulunamadığını kaydeder.

COALESCE: kayıp saati YALNIZCA henüz kurulmamışsa kuruluyor. Her koşuda üzerine yazmak, grace penceresini gerçek bir saat olmaktan çıkarıp hiç dolmayan bir sayaca çevirirdi.

func (*Store) MarkDirectorySeen

func (s *Store) MarkDirectorySeen(ctx context.Context, username string, at time.Time) error

MarkDirectorySeen, kullanıcının dizinde bulunduğunu kaydeder ve kayıp saatini SIFIRLAR.

func (*Store) MarkFirstKeyAdded

func (s *Store) MarkFirstKeyAdded(ctx context.Context, username string, at time.Time) error

MarkFirstKeyAdded, damgayı koyar. Zaten varsa DOKUNMAZ — ilk anahtarın anı, sonradan eklenenlerle kaymamalı.

func (*Store) Migrate

func (s *Store) Migrate(ctx context.Context) error

func (*Store) OpenSessions

func (s *Store) OpenSessions(ctx context.Context) ([]model.Session, error)

* OpenSessions, bitişi kaydedilmemiş bütün oturumları döner. * * ⚠️ AYRI BİR SORGU, Sessions(...200) ÜZERİNDE SÜZGEÇ DEĞİL. * * ÖLÇÜLEN ARIZA: panel "Active sessions" kartını en yeni 200 satırı * çekip istemcide `!ended_at` diye süzerek kuruyordu (Overview.tsx:158, * admin.go:739). Sabahtan beri açık duran bir oturumun üstüne 200 yeni * oturum bindiğinde, o oturum karttan tamamen düşüyor: yönetici onu * göremiyor, dolayısıyla kesemiyor da. Açık oturumları saymak için * geçmişin ilk 200 satırına bakmak, yanlış soruyu sormaktı. * * ⚠️ SINIRSIZ DEĞİL. Açık oturum sayısı bağlantı sınırlarıyla zaten * bağlı (listen.max_conns), ama uzlaştırılmamış hayalet satırlar * birikebiliyor (bkz. CloseOrphanSessions). Tavan, panelin bir kazayla * bütün tabloyu çekmesini engelliyor.

func (*Store) PendingByID

func (s *Store) PendingByID(ctx context.Context, id string) (PendingUser, error)

PendingByID, tek satır.

func (*Store) PendingMigrations

func (s *Store) PendingMigrations(ctx context.Context) (int, error)

func (*Store) Ping

func (s *Store) Ping(ctx context.Context) error

Ping, veritabanına ulaşılabildiğini doğrular.

⚠️ Sağlık ucu için var ve BAŞKA HİÇBİR ŞEY YAPMIYOR: bir sorgu çalıştırmak, kimlik istemeyen bir uçtan tetiklenebilen iş demekti.

func (*Store) ProvisionUser

func (s *Store) ProvisionUser(ctx context.Context, req ProvisionRequest) (model.User, error)

ProvisionUser, IdP kimliğinden kullanıcıyı oluşturur/günceller ve SSO rollerini senkronize eder. Dönen değer yetkileriyle birlikte kullanıcıdır.

SÖZLEŞME: hiçbir grup role eşleşmiyorsa kullanıcı OLUŞTURULMAZ ve ErrAccessDenied döner. "IdP'de hesabın olması postern'de hesabın olması demek değil" kuralının JIT çağındaki hâli — sadece "elle ekle" yerine "grubunu eşle" oldu. Yan faydası: users tablosu IdP'nin tüm dizinine dönüşmez, yalnızca gerçekten erişimi olanları içerir.

Var olan kullanıcı için: eşleşme kalmadıysa kullanıcı SİLİNMEZ (denetim kaydı ona bağlı) ama SSO rolleri temizlenir — erişim biter, iz kalır.

func (*Store) PublicKeyCounts

func (s *Store) PublicKeyCounts(ctx context.Context) (map[string]int, error)

* PublicKeyCounts, kullanıcı adı → anahtar sayısı. * * ⚠️ TEK SORGU, KULLANICI BAŞINA BİR SORGU DEĞİL. * * Liste bu sayıyı eskiden satır başına PublicKeys() çağırarak * topluyordu: 200 kullanıcılı bir kurulumda tek bir sayfa açılışı 201 * sorgu demekti ve dönen anahtarların BLOB'ları da okunup atılıyordu — * ekranda görünen tek şey bir sayıyken. * * Sayı ekranda kalmak zorunda: "kim hiç bağlanamıyor" sorusunun cevabı * bu ve onu göstermeyen bir liste, yöneticiyi her kullanıcıyı tek tek * açmaya gönderiyordu. * * Adı serbest bırakılmış satırlar dışarıda: onları Users() da * göstermiyor ve aynı ürünün iki farklı cevabı olmamalı.

func (*Store) PublicKeys

func (s *Store) PublicKeys(ctx context.Context, username string) ([]PublicKey, error)

func (*Store) PurgeAccount

func (s *Store) PurgeAccount(ctx context.Context, username string, at time.Time) (PurgeResult, error)

* PurgeAccount, kullanıcı ADINI ve diğer tanımlayıcıları serbest * bırakır — SATIRI SİLMEDEN. * * ⚠️ SATIR NEDEN KALIYOR: denetim kaydı ve oturum kayıtları kullanıcı * adını METİN olarak saklıyor. Satır yok olursa geçmişteki * "ayse.yilmaz" satırlarının kime ait olduğu cevapsız kalır — ve aynı * adı alan yeni kişiyle karışır. Kalan satır, "o ad şu tarihte * boşaltıldı" sorusunun cevabı. * * ⚠️ YALNIZCA 'deleted' HESAPLAR. Purge yaşam döngüsünün son adımı, bir * kısayol değil: aktif bir hesabın adını serbest bırakmak, o kişi hâlâ * kullanıyorken kimliğini elinden almak olurdu. * * Serbest bırakılanlar: kullanıcı adı, e-posta, iki kimlik bağı, * anahtarlar ve roller. Hepsi benzersizlik taşıyor ya da erişim * veriyor; biri kalırsa geri dönen kişi kendi hesabını açamaz.

func (*Store) PurgedAccounts

func (s *Store) PurgedAccounts(ctx context.Context) ([]PurgedAccount, error)

* PurgedAccounts, purge edilmiş kayıtları döner. * * ⚠️ Bu liste denetimin bir parçası: geçmişteki bir kullanıcı adının * kime ait olduğu sorusu ancak "o ad şu tarihte boşaltıldı" bilgisiyle * cevaplanabiliyor. Satırların silinmemesinin sebebi de bu.

func (*Store) QueueArchive

func (s *Store) QueueArchive(ctx context.Context, sessionID string) error

* QueueArchive, oturum için bekleyen bir arşiv satırı açar. * * Oturum kapanışında çağrılıyor. İkinci çağrı zararsız: satır zaten * varsa dokunulmuyor — özellikle archived_at'e, çünkü onu silmek * yüklenmiş bir kaydı yeniden bekleyene çevirirdi.

func (*Store) RecordPending

func (s *Store) RecordPending(ctx context.Context, p PendingUser) (string, error)

* RecordPending, bir başvuruyu kaydeder ve GÜNCEL durumu döner. * * ⚠️ RED YAPIŞKAN. Reddedilmiş bir kimlik yeniden giriş denediğinde * satır 'waiting'e DÖNMÜYOR; yalnızca last_seen ilerliyor. Dönseydi, * reddedilen kişi tekrar tekrar deneyerek kuyruğu doldurur ve * yöneticiyi aynı kararı vermeye zorlardı. * * ⚠️ Ad DEĞİŞSE de aynı satır: anahtar kimlik. Adla anahtarlanan bir * kuyrukta reddedilen kişi dizinde adını değiştirip yeniden * başvurabilirdi.

func (*Store) RecordTargetError

func (s *Store) RecordTargetError(ctx context.Context, targetName, reason string) error

RecordTargetError, başarısız bir bağlantı denemesini işaretler.

func (*Store) RecordTargetProbe

func (s *Store) RecordTargetProbe(ctx context.Context, targetName string, p model.TargetProbe) error

* RecordTargetProbe, hedefte KOMUT ÇALIŞTIRILARAK öğrenilenleri yazar. * * ⚠️ Yalnızca target_probe.enabled ile çağrılır. probed_at ayrı bir * damga: last_seen_at "bağlandık" demek, probed_at "makineye dokunduk" * demek ve ikisi aynı şey değil.

func (*Store) RecordTargetProbeAttempt

func (s *Store) RecordTargetProbeAttempt(ctx context.Context, targetName string, at time.Time) error

* RecordTargetProbeAttempt, hedefe DOKUNULDUĞUNU kaydeder — ne * öğrenildiğini değil. * * ⚠️ NEDEN VAR: probed_at yalnızca BAŞARILI yoklamada yazılıyordu, oysa * target_probe.refresh kapısı ona bakıyor. Yani cevap üretmeyen bir * yoklama kapıyı hiç kapatmıyor ve HER KANALDA yeniden koşuyordu: * ölçüldü, tek bağlantıda beş kanal = beş yoklama ve (denetim satırı * denemeye taşındıktan sonra) beş defter satırı, oysa başarılı yoklamada * beş kanal = bir satır. * * Bedeli üç yerde: hedefin günlüğüne kullanıcının adına tekrar tekrar * komut düşüyor, denetim defteri aynı cümleyle şişiyor, ve refresh * ayarı söylediği şeyi yapmıyor. * * ⚠️ SEMANTİK DOĞRU: "ProbedAt sıfırsa hedefe hiç dokunulmadı" diyor * (model.TargetFacts) — cevapsız kalan bir yoklama da DOKUNUYOR: exec * kanalı açılıyor ve komutlar çalışıyor. Zaten denetlenmesinin sebebi * bu. Facts'e dokunulmuyor: öğrenilmemiş bir şey yazılmış gibi * görünmesin.

func (*Store) RecordTargetSeen

func (s *Store) RecordTargetSeen(ctx context.Context, targetName string, f model.TargetFacts) error

RecordTargetSeen, başarılı bir bağlantıdan sonra öğrenilenleri yazar.

⚠️ ÇAĞIRAN BU HATAYI OTURUMA YANSITMAMALI. Burası bir gözlem kaydı; yazılamaması can sıkıcıdır ama kullanıcının oturumunu düşürmek için sebep değil. Denetim kaydı (sessions, admin_log) başka bir şey ve onun yazılamaması oturumu düşürür — ayrım kasıtlı.

func (*Store) RecordUnmappedGroups

func (s *Store) RecordUnmappedGroups(ctx context.Context, groups []string) error

RecordUnmappedGroups, eşlenmemiş grupları teşhis tablosuna işler.

Hata döndürmez: bu bir teşhis kaydı, girişi düşürmesi için sebep yok. Sorun çıkarsa çağıran loglar.

func (*Store) RefuseIfDeleted

func (s *Store) RefuseIfDeleted(ctx context.Context, username string) error

* RefuseIfDeleted, silinmiş bir hesabın giriş yapmasını engeller. * * ⚠️ 'inactive' BURADA REDDEDİLMİYOR ve bu tasarımın kendisi: * pasifleşme "kaynak bir süredir doğrulamadı" demek ve BAŞARILI GİRİŞİN * kendisi o doğrulama. Reddetseydik, geri dönüş yolu bir yöneticiden * geçerdi ve tatilden dönen herkes kuyruğa girerdi. * * 'deleted' farklı: orada uzun bir sessizlik ya da bir insan kararı var * ve onu bir girişin sessizce bozması, "silindi" demenin anlamını yok * ederdi.

func (*Store) RefuseIfDeletedByID

func (s *Store) RefuseIfDeletedByID(ctx context.Context, id string) error

* RefuseIfDeletedByID, RefuseIfDeleted'in KİMLİK ile çalışan hâli. * * ⚠️ NEDEN AD DEĞİL KİMLİK: purge, adı serbest bırakıp satırı * 'purged:<id>' + deleted yapıyor. Ada bakan bir kontrol, ad yeniden * yaratıldığında YENİ satıra çözülüp nil döner — ölçülen sızıntı buydu * (CLI purge ayrı süreç, bellekteki oturumu düşüremiyor). Kimliğe bakan * kontrol, oturumun bağlı olduğu satırı görüyor: purge sonrası o satır * deleted, yani ret KALICI. * * Kimlik hiç yoksa (satır gerçekten silinmiş) ErrNotFound.

func (*Store) RejectPending

func (s *Store) RejectPending(ctx context.Context, id, reason, by string) error

* RejectPending, başvuruyu reddeder ve SATIRI BIRAKIR. * * ⚠️ Silinmiyor: silinseydi aynı kişi bir sonraki girişinde yeniden * 'waiting' olarak belirir ve red hiçbir şey ifade etmezdi. Sebep de * saklanıyor — aynı kararı ikinci kez veren yönetici, ilkinin niye * verildiğini görmeli.

func (*Store) RemoveGroupMapping

func (s *Store) RemoveGroupMapping(ctx context.Context, externalGroup, roleName string) error

RemoveGroupMapping, eşlemeyi kaldırır. Yoksa ErrNotFound.

⚠️ Bu, kullanıcıların rollerini ANINDA değiştirmez: mevcut SSO atamaları bir sonraki girişte yenilenir. Yetkiyi hemen kesmek gerekiyorsa rolü ya da kullanıcının erişimini ayrıca ele almak gerekir.

func (*Store) RemoveLocalCredential

func (s *Store) RemoveLocalCredential(ctx context.Context, username string) error

RemoveLocalCredential, kimlik bilgisini siler. Hesap kalır.

func (*Store) RemovePublicKey

func (s *Store) RemovePublicKey(ctx context.Context, username string, keyBlob []byte) error

RemovePublicKey, anahtarı kullanıcıdan kaldırır. Kullanıcı yoksa ya da anahtar BU kullanıcıya ait değilse ErrNotFound — başka birinin anahtarını "benimkiymiş gibi" silmek sessizce başarılı OLMAMALI. keyBlob ham bayt; base64 çevrimi içeride, AddPublicKey ile simetrik.

func (*Store) ReplaceLocalCredential

func (s *Store) ReplaceLocalCredential(ctx context.Context, username, verifier, by string) (replaced bool, err error)

* ReplaceLocalCredential, panelden verilen kimlik bilgisi. Var olanın * ÜSTÜNE YAZAR ve yalnızca YÖNETİCİ OLMAYAN hesaplar için çalışır. * * ⚠️ AddLocalCredential'IN ÜSTÜNE YAZMAMA KURALI KALDIRILMADI, YALNIZCA * BURADA GEÇERLİ DEĞİL — ve iki fonksiyonun ayrı durmasının sebebi tam * olarak bu. Oradaki kural şunu koruyordu: yazma yetkisi olan biri, * kurulumun tek yöneticisinin kimlik bilgisini sessizce döndüremesin. * Aşağıdaki `is_admin = FALSE` koşulu o korumayı aynen sürdürüyor — * yöneticiye bu yoldan dokunulamıyor, hiç. * * Üstüne yazmak burada ise ZORUNLU: "kullanıcı parolasını unuttu" * panelden çözülemezse, panelden sır verme özelliğinin var olma sebebi * — yöneticiyi host kabuğuna göndermemek — ilk gerçek olayda çöker. * * ⚠️ KOŞUL SQL'İN İÇİNDE, ÇAĞIRANDA DEĞİL. Çağıranın `if u.Admin` * yazması unutulabilir; buradaki WHERE unutulamaz. Üstüne bir de göç * 026'nın bileşik yabancı anahtarı var: holder_is_admin uydurulamıyor. * * Dönen değer: var olan bir kimlik bilgisinin üstüne mi yazıldı. * Denetim kaydı için — "verildi" ile "değiştirildi" farklı olaylar.

func (*Store) RevokeRole

func (s *Store) RevokeRole(ctx context.Context, username, roleName string) error

RevokeRole, kullanıcıdan rolü geri alır. Kullanıcı ya da rol yoksa ErrNotFound; bağ zaten yoksa SESSİZ no-op (AssignRole'un aynası).

func (*Store) RevokeTarget

func (s *Store) RevokeTarget(ctx context.Context, roleName, targetName string) error

RevokeTarget, rolden hedef erişimini geri alır. GrantTarget'ın aynası.

func (*Store) RoleGrantSource

func (s *Store) RoleGrantSource(ctx context.Context, username, roleName string) (source string, found bool, err error)

* RoleGrantSource, bir atamanın NEREDEN geldiğini söyler: "manual" ya * da "sso". found=false, böyle bir atama yok demek. * * ⚠️ NEDEN GEREKLİ: AssignRole'un ON CONFLICT dalı source'u koşulsuz * 'manual' yapıyor, SyncRoles ise yalnızca source='sso' satırlarını * siliyor. Yani dizinden gelen bir rolü elle "yeniden vermek", o rolü * senkronizasyonun erişemeyeceği bir yere taşıyor: kişi gruptan * çıkarıldığında rol ÜZERİNDE KALIYOR ve hiçbir otomatik yol onu geri * alamıyor. Bu okuma olmadan çağıran, sessizce kalıcı yetki üretiyor. * * Kimseyi engellemek için değil, SÖYLEYEBİLMEK için var.

func (*Store) Roles

func (s *Store) Roles(ctx context.Context) ([]model.Role, error)

Roles, tüm rolleri hedefleriyle, ada göre sıralı döner; hedefsiz rol boş Targets ile gelir.

func (*Store) RolesForGroups

func (s *Store) RolesForGroups(ctx context.Context, groups []string) (roles, unmapped []string, err error)

RolesForGroups, verilen dış grup adlarının karşılığı rolleri ve eşleşmeyen grupları döner.

Eşleşmeyenler çağırana veriliyor ki kaydedebilsin (RecordUnmappedGroups) ve yönetici neyi eşlemediğini görebilsin.

func (*Store) Rollback

func (s *Store) Rollback(ctx context.Context) error

func (*Store) SchemaVersion

func (s *Store) SchemaVersion(ctx context.Context) (int, error)

func (*Store) SeenGroupNames

func (s *Store) SeenGroupNames(ctx context.Context) ([]string, error)

* SeenGroupNames, postern'in BUGÜNE KADAR gördüğü grup adları. * * İki kaynaktan: kullanıcılara gerçekten atanmış SSO rollerinin * eşlemeleri, ve eşlenmemiş grup teşhis tablosu. İkisi birlikte, * "bu ad hiç geldi mi" sorusunun elimizdeki en iyi cevabı. * * ⚠️ Kesin bir liste DEĞİL ve öyle sunulmamalı: bir kaynağa "hangi * gruplar var" diye genel olarak sorulamıyor (OIDC claim'i * listelenemez). Bu yüzden çağıran bunu bir teşhis olarak kullanıyor, * bir iddia olarak değil.

func (*Store) Session

func (s *Store) Session(ctx context.Context, id string) (model.Session, error)

Session, tek bir oturumu kimlik ADLARIYLA döner. Yoksa ErrNotFound.

Sessions'ın sorgusunun WHERE s.id = $1 hâli; NULL/zaman çevrimleri de birebir aynı desen.

func (*Store) SessionFiles

func (s *Store) SessionFiles(ctx context.Context, sessionID string) ([]SessionFile, error)

SessionFiles, bir oturumun dosya olaylarını zaman sırasıyla döner.

func (*Store) Sessions

func (s *Store) Sessions(ctx context.Context, username string, limit int) ([]model.Session, error)

func (*Store) SetAccountState

func (s *Store) SetAccountState(ctx context.Context, username, state string) error

* SetAccountState, durumu elle değiştirir (panel/CLI). * * ⚠️ 'deleted'ten 'active'e dönüş SERBEST: yanlışlıkla silinmiş bir * hesabın geri gelememesi, tek tıkla kalıcı bir kayıp demekti. Fiziki * silme zaten yok.

func (*Store) SetChosenPassword

func (s *Store) SetChosenPassword(ctx context.Context, username, verifier string, at time.Time) error

* SetChosenPassword, kullanıcının SEÇTİĞİ parolayı yazar. * * ⚠️ AddLocalCredential'DAN AYRI. O, üstüne yazmayı REDDEDİYOR ve bu * kasıtlı (bkz. oradaki not): var olan bir sırrı sessizce döndürmek, * elindeki değerle çalışan operatörü dışarıda bırakırdı. Burada tam * tersi isteniyor — amaç zaten üstüne yazmak. İki niyeti tek fonksiyona * bayrakla toplamak, o korumayı bir gün yanlışlıkla açık bırakırdı. * * ⚠️ YÖNETİCİ KONTROLÜ BURADA YOK. Kasıtlı: kuralı veritabanı tutuyor * (göç 026'daki CHECK). Buraya bir `if` yazmak, kuralın ikinci bir * kopyası olurdu ve iki kopyadan biri er ya da geç geride kalır. * Yönetici hesabında bu çağrı 23514 ile düşüyor ve çağıran onu * ErrAdminPasswordRefused olarak görüyor.

func (*Store) SetGroupAdmin

func (s *Store) SetGroupAdmin(ctx context.Context, username string, admin bool) error

* SetGroupAdmin, dizin GRUBUNDAN gelen yönetici yetkisini uygular. * * ⚠️ CLI'IN VERDİĞİNE DOKUNMAZ. WHERE koşulundaki * `admin_via IS DISTINCT FROM 'cli'` bunun için: acil durum diye elle * açılmış bir yöneticinin, dizinde o grubu görülmediği için sessizce * yetkisini kaybetmesi, tam olarak kaçınılması gereken şey — ve * kaybettiği an, onu geri verecek kişinin de kapısı kapanmış olurdu. * * Rol modelindeki source='sso' / 'manual' ayrımının aynısı: her * mekanizma yalnızca KENDİ verdiğini geri alabiliyor.

func (*Store) SetRecordingPathForTest

func (s *Store) SetRecordingPathForTest(ctx context.Context, sessionID, path string) error

* SetRecordingPathForTest, kayıt yolunu doğrudan değiştirir. * * ⚠️ YALNIZCA TESTLER İÇİN. Üretimde bu sütunu yazan tek yer * StartSession; keyfi bir yol yazabilen bir API, veritabanına * ulaşan birine dosya okuma/yükleme yetkisi vermek olurdu. Test, * arşivleyicinin tam da o saldırıyı reddettiğini ölçüyor.

func (*Store) SetSearchTimeoutForTest

func (s *Store) SetSearchTimeoutForTest(d time.Duration)

* SetSearchTimeoutForTest, sunucu tarafı sınırı kısaltır. * * ⚠️ YALNIZCA TESTLER İÇİN. Sınırın gerçekten UYGULANDIĞINI ölçmenin * tek yolu onu aşan bir sorgu çalıştırmak; 5 saniyelik varsayılanla o * test her CI koşusuna beş saniye eklerdi ve büyük ihtimalle hiç * yazılmazdı. Ölçülmeyen bir koruma, olmayan bir korumadır.

func (*Store) SetSetting

func (s *Store) SetSetting(ctx context.Context, key, value string, encrypt bool, actor string) error

SetSetting, ayarı yazar. encrypt=true ise değer mühürlenerek saklanır.

encrypt=true iken anahtar yoksa REDDEDİLİR — düz metin yazıp "şifreledim" sanmak bu paketin bütün amacını sessizce boşa çıkarır.

func (*Store) SetTargetLabel

func (s *Store) SetTargetLabel(ctx context.Context, targetName, key, value, actor, via string) error

SetTargetLabel, etiketi ekler ya da değerini değiştirir.

Denetim satırı BURADA yazılıyor, çağıranda değil: etiket hedefin nasıl bulunacağını belirliyor ve "prod" etiketini bir makineden alıp başkasına takan kişinin izi kalmalı.

func (*Store) SetUserAdmin

func (s *Store) SetUserAdmin(ctx context.Context, username string, admin bool) error

SetUserAdmin, uygulama yönetim yetkisini verir ya da alır. Kullanıcı yoksa ErrNotFound.

func (*Store) SetUserEmail

func (s *Store) SetUserEmail(ctx context.Context, username, email string) error

SetUserEmail, kullanıcının e-postasını değiştirir; boş string adresi SİLER (NULL). Kullanıcı yoksa ErrNotFound; adres başka bir kullanıcıda kayıtlıysa ErrConflict (users.email UNIQUE — OIDC eşleşmesi tekil kalmalı).

"user add"in reddettiği örtük değişikliğin AÇIK hâli: yönetici ne yaptığını komutun adıyla söylüyor.

func (*Store) SetUserOSUser

func (s *Store) SetUserOSUser(ctx context.Context, username, osUser string) error

SetUserOSUser, kullanıcının hedeflerdeki hesabını değiştirir. Boş değer şemadaki CHECK'e takılır; kullanıcı yoksa ErrNotFound.

⚠️ Bu, bir sonraki oturumdan itibaren kesilecek SERTİFİKALARIN principal'ını değiştirir — geçmiş denetim kayıtlarına dokunmaz (sessions.os_user o günkü kararı saklar; sebebi şemada yazıyor).

func (*Store) SetUserSSOOnly

func (s *Store) SetUserSSOOnly(ctx context.Context, username string, ssoOnly bool) error

SetUserSSOOnly, kullanıcının yalnızca kimlik sağlayıcı üzerinden girebilmesini açar/kapatır. Kullanıcı yoksa ErrNotFound.

func (*Store) Setting

func (s *Store) Setting(ctx context.Context, key string) (string, error)

func (*Store) Settings

func (s *Store) Settings(ctx context.Context) ([]SettingView, error)

Settings, ada göre sıralı ayar listesi döner — ŞİFRELİ DEĞERLER MASKELENMİŞ olarak.

⚠️ Bu fonksiyonun çıktısı admin API'sine gidiyor: şifreli bir ayarın değeri ASLA dönmez, maskelenir. Sır yazılır ama okunmaz.

func (*Store) SourceAccountCount

func (s *Store) SourceAccountCount(ctx context.Context) (int, error)

SourceAccountCount, kaynaktan gelen toplam hesap sayısı — patlama yarıçapı oranının paydası.

func (*Store) StaleAccounts

func (s *Store) StaleAccounts(ctx context.Context, olderThan time.Time, state string) ([]StaleAccount, error)

* StaleAccounts, kaynağın BELİRLİ BİR SÜREDİR doğrulamadığı hesapları * döner. * * ⚠️ KAPSAM: yalnızca kaynaktan gelen hesaplar (sso_only ya da dizine * bağlı). Yerel hesaplar DIŞARIDA ve bu kapsam daraltması özelliğin * otomasyon için güvenli olmasının tek sebebi: CI ve servis hesapları * yerel açılıyor, hiçbir kaynağa sorulamıyor ve "doğrulanmamış olmaları" * normal. Onları da kapsayan bir sayaç, her otomasyonu süre dolunca * keserdi.

func (*Store) StartSession

func (s *Store) StartSession(ctx context.Context, rec SessionStart) error

func (*Store) StartSyncRun

func (s *Store) StartSyncRun(ctx context.Context, source, trigger string, dryRun bool) (int64, error)

StartSyncRun, koşuyu açar ve id'sini döner.

func (*Store) SyncCandidates

func (s *Store) SyncCandidates(ctx context.Context) ([]SyncCandidate, error)

SyncCandidates, senkronizasyona TABİ kullanıcıları döner.

  • ⚠️ KAPSAM: sso_only OLANLAR ARTI DİZİNE BAĞLI OLANLAR. *
  • Eskiden yalnızca sso_only = TRUE idi ve bu, freshen'da düzeltilen
  • hatanın buradaki eşiydi: yetkisi dizinden gelen bir kullanıcı
  • (dir_subject dolu, sso_only false) HİÇ senkronize edilmiyordu. Yani
  • dizinden silinen kişi, bir daha hiç giriş yapmadığı sürece rollerini
  • süresiz koruyordu — ve döngünün var olma sebebi tam olarak o kişiydi. *
  • ⚠️ Servis hesapları (otomasyon, CI) HÂLÂ DIŞARIDA ve genişletme bunu
  • bozmuyor: onların ne sso_only'si var ne dizin kimliği. Dizinde
  • "bulunamamaları" normal ve döngünün onlara dokunmaması şart (göç 008
  • tam olarak bu ayrım için).

func (*Store) SyncRoles

func (s *Store) SyncRoles(ctx context.Context, username string, roleNames []string) error

SyncRoles, kullanıcının SSO kaynaklı rollerini IdP'nin söylediğiyle DEĞİŞTİRİR. Elle atanmış roller (source='manual') etkilenmez.

Her SSO girişinde çağrılır: gruptan çıkarılan kullanıcı yetkisini o an kaybeder, yeni gruba eklenen o an kazanır.

func (*Store) SyncRuns

func (s *Store) SyncRuns(ctx context.Context, limit int) ([]SyncRun, error)

SyncRuns, son koşuları yeniden eskiye döner.

func (*Store) TOTP

func (s *Store) TOTP(ctx context.Context, username string) (TOTPCredential, error)

TOTP, hesabın ikinci faktörünü döner. Yoksa ErrNotFound.

func (*Store) Target

func (s *Store) Target(ctx context.Context, name string) (model.Target, error)

func (*Store) TargetFacts

func (s *Store) TargetFacts(ctx context.Context, targetName string) (model.TargetFacts, error)

TargetFacts, tek bir hedefin gözlemleri. Hiç bağlanılmamışsa sıfır değer döner — ErrNotFound DEĞİL: "henüz bağlanılmadı" bir hata değil, hedefin geçerli bir hâli.

func (*Store) TargetLabels

func (s *Store) TargetLabels(ctx context.Context, targetName string) (map[string]string, error)

TargetLabels, tek bir hedefin etiketleri.

func (*Store) Targets

func (s *Store) Targets(ctx context.Context) ([]model.Target, error)

func (*Store) TouchLocalCredential

func (s *Store) TouchLocalCredential(ctx context.Context, username string, at time.Time) error

TouchLocalCredential, son kullanım anını damgalar.

Hatası girişi DÜŞÜRMEZ: çağıran loglar. Kullanım damgası bir teşhis bilgisi ve onun yazılamaması, kimliği doğrulanmış bir kullanıcıyı dışarıda bırakmak için sebep değil.

func (*Store) TryLockSync

func (s *Store) TryLockSync(ctx context.Context) (release func(), acquired bool, err error)

TryLockSync, senkronizasyon kilidini DENEYEREK alır.

İki postern süreci aynı veritabanına bakıyorsa ikisi de senkronize eder ve patlama yarıçapı sayaçlarını BAĞIMSIZ hesaplar — yani tavanlar ikiye bölünmüş olur ve ikisi birden geçebilir.

pg_try_advisory_lock BEKLEMEZ: alamadıysa bu koşu atlanır. Beklemek, zamanlayıcı tikleri kuyruğa dizmek demek olurdu.

⚠️ Kilit SABİTLENMİŞ bir bağlantıda tutuluyor: havuzdan alınan bir bağlantı koşunun ortasında geri verilebilir ve kilit sessizce düşerdi.

func (*Store) UnbindDirIdentity

func (s *Store) UnbindDirIdentity(ctx context.Context, username string) error

UnbindDirIdentity, bağı koparır.

⚠️ Kurtarma yolu ve var olması ŞART: dizinde silinip yeniden açılan bir kişi YENİ bir kimlik alır (ölçüldü) ve eski bağ onu kendi hesabından kilitler. Bunu çözecek bir komut olmazsa tek çıkış veritabanına elle girmek olurdu.

func (*Store) UnmappedGroups

func (s *Store) UnmappedGroups(ctx context.Context) ([]UnmappedGroup, error)

UnmappedGroups, eşlenmemiş grupları en çok görülenden aza doğru döner.

func (*Store) UseSecretBox

func (s *Store) UseSecretBox(box *secret.Box)

UseSecretBox, şifreli ayarları açıp mühürleyecek anahtarı bağlar.

Store bunsuz da çalışır: şifresiz ayarlar okunur/yazılır, şifreli olana dokunulduğunda hata verilir. CLI'ın çoğu komutu sır gerektirmiyor ve anahtar dosyası olmadan da çalışabilmeli.

func (*Store) UseTOTPStep

func (s *Store) UseTOTPStep(ctx context.Context, username string, step int64) error

* UseTOTPStep, bir adımı TÜKETİR ve daha önce kullanılmışsa reddeder. * * ⚠️ TEK BİR İFADEDE KARŞILAŞTIR-VE-YAZ. Önce okuyup sonra yazan bir * uygulama, aynı kodla gönderilen İKİ eşzamanlı isteğin ikisini de * geçirirdi: her ikisi de eski değeri okur, her ikisi de kabul eder. * Tekrar korumasının bütün anlamı tam olarak o yarışı kapatmak. * * `last_step < $2` aynı zamanda pencere içindeki DAHA ESKİ adımları da * kapatıyor: adım N kullanıldıysa, hâlâ geçerli olan N-1 kodu da ölür.

func (*Store) User

func (s *Store) User(ctx context.Context, username string) (model.User, error)

func (*Store) UserByDirSubject

func (s *Store) UserByDirSubject(ctx context.Context, subject string) (model.User, error)

UserByDirSubject, dizin kimliğine bağlı kullanıcıyı döner. Bağlı hesap yoksa ErrNotFound.

func (*Store) UserByEmail

func (s *Store) UserByEmail(ctx context.Context, email string) (model.User, error)

func (*Store) UserByIdPSubject

func (s *Store) UserByIdPSubject(ctx context.Context, issuer, subject string) (model.User, error)

UserByIdPSubject, (issuer, subject) çiftine bağlı kullanıcıyı döner.

func (*Store) UserByNameFold

func (s *Store) UserByNameFold(ctx context.Context, name string) (string, error)

* UserByNameFold, adı HARF DUYARSIZ arar ve bulunan gerçek adı döner. * * ⚠️ KULLANICI ADLARI 019'DAN BERİ HARF DUYARSIZ BENZERSİZ. Eski yorum * bunun tersini söylüyordu ("Ops ile ops iki ayrı hesap olabilir") ve * o artık yanlış: göç 019 lower(username) üzerinde tekil bir indeks * kurdu, tam da bir harf farkıyla ikinci bir yönetici ve ikinci bir * canlı sır yaratılmasını önlemek için. * * Bu fonksiyon o tekilliğin okuma tarafı: bootstrap, adı ne yazımla * yazılmış olursa olsun tek hesabı bulmalı. * * lower() indeksi VAR (019) ve users.username ciColumns'ta — sorgu bir * tablo taraması yapmıyor. Eski yorum "indeks yok" diyordu; o da yanlış.

func (*Store) UserByPublicKey

func (s *Store) UserByPublicKey(ctx context.Context, keyBlob []byte) (model.User, error)

func (*Store) UserProfile

func (s *Store) UserProfile(ctx context.Context, username string) (UserProfile, error)

UserProfile, detay alanlarını TEK sorguda döner.

func (*Store) Users

func (s *Store) Users(ctx context.Context) ([]model.User, error)

Users, tüm kullanıcıları rolleriyle birlikte, ada göre sıralı döner.

User'ın sorgusunun WHERE'siz hâli; gruplama iki seviyeli — önce kullanıcıya, sonra role. Desen User'dakiyle aynı, yalnızca indeks map'leri kullanıcı boyutu kazanıyor.

Hiç kullanıcı yoksa boş dilim, hata değil.

type SyncCandidate

type SyncCandidate struct {
	Username string
	Email    string

	// MissingSince sıfırsa kullanıcı en son bakıldığında dizinde vardı.
	MissingSince time.Time

	SSORoles    int
	ManualRoles int

	/*
	 * DirSubject, hesabın bağlı olduğu KARARLI dizin kimliği; bağlı
	 * değilse boş.
	 *
	 * ⚠️ Döngü, varsa BUNUNLA aramalı. Adla arama, dizinde yeniden
	 * adlandırılan kişiyi SİLİNMİŞ kişiden ayırt edemiyor — ikisi de
	 * PresenceAbsent döner ve döngü ikincisini rol iptaline çevirir.
	 * Yani İK'nın bir soyadı güncellemesi, kimsenin fark etmediği bir
	 * yerde erişim kaybına dönüşüyordu.
	 */
	DirSubject string
}

SyncCandidate, senkronizasyonun bakacağı bir kullanıcı.

type SyncRun

type SyncRun struct {
	ID         int64
	StartedAt  time.Time
	FinishedAt time.Time
	Source     string
	Trigger    string
	Outcome    string
	Reason     string

	Considered   int
	Present      int
	Absent       int
	Unknown      int
	Revoked      int
	RolesChanged int

	DryRun bool
}

SyncRun, bir senkronizasyon koşusunun kaydı.

type TOTPCredential

type TOTPCredential struct {
	// ⚠️ Secret JSON'a ÇIKMIYOR. Bu değer kod üretmeye yeter; bir
	// listeleme ucundan sızması, ikinci faktörü ikinci faktör olmaktan
	// çıkarır. Yalnızca kayıt akışında, bir kez gösteriliyor.
	Secret string `json:"-"`

	Confirmed   bool      `json:"confirmed"`
	ConfirmedAt time.Time `json:"confirmed_at,omitzero"`
	CreatedAt   time.Time `json:"created_at"`
	LastUsedAt  time.Time `json:"last_used_at,omitzero"`
}

TOTPCredential, bir hesabın ikinci faktörü.

type UnmappedGroup

type UnmappedGroup struct {
	Name      string
	LastSeen  time.Time
	SeenCount int
}

UnmappedGroup, teşhis listesindeki bir kayıt.

type UserProfile

type UserProfile struct {
	Email string
	// AdminVia: "cli", "group" ya da boş. Yöneticiliğin KAYNAĞI, çünkü
	// panelin kaldırabildiği ile kaldıramadığı ayrı şeyler.
	AdminVia  string
	State     string
	Confirmed time.Time
	// Purged: adı serbest bırakılmış. Satır yalnızca denetim kaydının
	// okunabilir kalması için duruyor.
	Purged bool

	// HasCredential false ise aşağıdaki alanlar anlamsız: hesabın
	// postern'de doğrulanabilir bir değeri yok, kimliği başka yerden
	// geliyor.
	HasCredential  bool
	CredChosen     bool
	CredMustChange bool
	CredCreatedAt  time.Time
	CredCreatedBy  string
	CredLastUsed   time.Time
}

* UserProfile, kullanıcı DETAY ekranının ihtiyacı. * * ⚠️ NEDEN AYRI BİR SORGU: model.User bilerek dar tutuldu — yetki * kararlarının ihtiyacı olan alanları taşıyor ve SSH kapısında her * oturumda okunuyor. E-posta, hesap durumu ve kimlik bilgisi meta * verisi orada hiçbir kararı etkilemiyor; oraya eklemek, hiç * kullanılmayan alanları en sıcak yoldan geçirmek olurdu. * * Buradaki alanların ortak yanı hepsinin bir TEŞHİS sorusuna cevap * vermesi: "neden giremiyor", "yöneticiliği nereden geliyor", "bu * hesabın nasıl bir giriş bilgisi var".

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL