package lib // ============================================================================= // LEGACY STUBS — temporary bridges while callers are migrated to dbcore.go // // Each stub logs its call so we can find and replace callers. // Delete this file when all callers use the new core. // ============================================================================= import ( "fmt" "log" "os" "path/filepath" ) // --- Legacy types --- type EntryFilter struct { DossierID string Type string Value string SearchKey string SearchKey2 string FromDate int64 ToDate int64 Limit int } type AccessFilter struct { AccessorID string TargetID string Status *int } type DossierQueryRow struct { Dossier Category int `db:"category"` EntryCount int `db:"entry_count"` } type DossierAccess struct { Access } // --- Entry convenience wrappers (all delegate to core with RBAC) --- func EntryGet(ctx *AccessContext, id string) (*Entry, error) { accessorID := "" if ctx != nil { accessorID = ctx.AccessorID } return entryGetByID(accessorID, id) } func EntryQuery(ctx *AccessContext, dossierID string, category int, typ, parent string) ([]*Entry, error) { accessorID := "" if ctx != nil { accessorID = ctx.AccessorID } f := &Filter{Category: category} if typ != "" { f.Type = typ } if parent != "" && parent != "*" { f.ParentID = parent } return EntryRead(accessorID, dossierID, f) } func EntryList(accessorID string, parent string, category int, f *EntryFilter) ([]*Entry, error) { dossierID := "" filter := &Filter{Category: category, ParentID: parent} if f != nil { dossierID = f.DossierID filter.Type = f.Type filter.SearchKey = f.SearchKey filter.SearchKey2 = f.SearchKey2 filter.FromDate = f.FromDate filter.ToDate = f.ToDate filter.Limit = f.Limit } return EntryRead(accessorID, dossierID, filter) } func EntryQueryOld(dossierID string, category int, typ string) ([]*Entry, error) { return EntryRead("", dossierID, &Filter{Category: category, Type: typ}) } func EntryCount(ctx *AccessContext, dossierID string, category int, typ string) (int, error) { entries, err := EntryQuery(ctx, dossierID, category, typ, "*") if err != nil { return 0, err } return len(entries), nil } func EntryChildren(dossierID, parentID string) ([]*Entry, error) { return EntryRead("", dossierID, &Filter{Category: -1, ParentID: parentID}) } func EntryChildrenByType(dossierID, parentID string, typ string) ([]*Entry, error) { return EntryRead("", dossierID, &Filter{Category: -1, ParentID: parentID, Type: typ}) } func EntryTypes(dossierID string, category int) ([]string, error) { entries, err := EntryRead("", dossierID, &Filter{Category: category}) if err != nil { return nil, err } seen := map[string]bool{} var types []string for _, e := range entries { if !seen[e.Type] { seen[e.Type] = true types = append(types, e.Type) } } return types, nil } // --- Dossier stubs --- func DossierQuery(accessorID string) ([]*DossierQueryRow, error) { // Get all accessible dossier profiles via RBAC dossierEntries, err := EntryRead(accessorID, "", &Filter{Category: 0}) if err != nil { return nil, err } var rows []*DossierQueryRow for _, de := range dossierEntries { d := *DossierFromEntry(de) // Count entries by category for this dossier allEntries, err := EntryRead(accessorID, de.DossierID, &Filter{Category: -1}) if err != nil { continue } catCounts := map[int]int{} for _, e := range allEntries { if e.Category > 0 { catCounts[e.Category]++ } } if len(catCounts) == 0 { // Still include dossier even with no entries rows = append(rows, &DossierQueryRow{Dossier: d}) } else { for cat, count := range catCounts { rows = append(rows, &DossierQueryRow{Dossier: d, Category: cat, EntryCount: count}) } } } return rows, nil } // --- Access stubs --- func AccessGet(accessorID, targetID string) (*Access, error) { var grants []Access if err := dbQuery( "SELECT AccessID, DossierID, GranteeID, EntryID, Ops, Relation, CreatedAt FROM access WHERE GranteeID = ? AND DossierID = ?", []any{accessorID, targetID}, &grants, ); err != nil { return nil, err } // Prefer dossier-level grant (EntryID == dossierID) for _, g := range grants { if g.EntryID == targetID { return &g, nil } } if len(grants) > 0 { return &grants[0], nil } return nil, nil } func AccessRemove(accessorID, targetID string) error { log.Printf("[STUB] AccessRemove(accessor=%s, target=%s)", accessorID, targetID) return nil } func AccessList(f *AccessFilter) ([]*Access, error) { if f == nil { return nil, nil } var grants []*Access if f.AccessorID != "" { return grants, dbQuery( "SELECT AccessID, DossierID, GranteeID, EntryID, Relation, Ops, CreatedAt FROM access WHERE GranteeID = ?", []any{f.AccessorID}, &grants, ) } if f.TargetID != "" { return grants, dbQuery( "SELECT AccessID, DossierID, GranteeID, EntryID, Relation, Ops, CreatedAt FROM access WHERE DossierID = ?", []any{f.TargetID}, &grants, ) } return nil, nil } func AccessListByAccessor(granteeID string) ([]*Access, error) { log.Printf("[STUB] AccessListByAccessor(grantee=%s)", granteeID) return nil, nil } func AccessListByTargetWithNames(targetID string) ([]map[string]interface{}, error) { log.Printf("[STUB] AccessListByTargetWithNames(target=%s)", targetID) return nil, nil } func AccessUpdateTimestamp(granteeID, dossierID string) error { log.Printf("[STUB] AccessUpdateTimestamp(grantee=%s, dossier=%s)", granteeID, dossierID) return nil } func AccessGrantRole(dossierID, granteeID, role string) error { log.Printf("[STUB] AccessGrantRole(dossier=%s, grantee=%s, role=%s)", dossierID, granteeID, role) return nil } // --- Audit stubs --- func auditWrite(a *AuditEntry) { if a.AuditID == "" { a.AuditID = NewID() } if a.Timestamp == 0 { a.Timestamp = nowUnix() } db.Exec(`INSERT INTO audit (AuditID, Actor1ID, Actor2ID, TargetID, Action, Details, RelationID, Timestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, a.AuditID, a.Actor1ID, a.Actor2ID, a.TargetID, PackStr(a.Action), PackStr(a.Details), a.RelationID, a.Timestamp, ) } func AuditAdd(a *AuditEntry) error { auditWrite(a) return nil } func AuditLog(actor1ID string, action string, targetID string, details string) { auditWrite(&AuditEntry{Actor1ID: actor1ID, Action: action, TargetID: targetID, Details: details}) } func AuditLogFull(actor1ID, actor2ID, targetID string, action, details string, relationID int) { auditWrite(&AuditEntry{Actor1ID: actor1ID, Actor2ID: actor2ID, TargetID: targetID, Action: action, Details: details, RelationID: relationID}) } // --- Object I/O (RBAC enforced, Pack/Unpack for encryption) --- func ObjectWrite(ctx *AccessContext, dossierID, entryID string, data []byte) error { accessorID := "" if ctx != nil { accessorID = ctx.AccessorID } if !CheckAccess(accessorID, dossierID, entryID, PermWrite) { return fmt.Errorf("access denied") } path := ObjectPath(dossierID, entryID) if err := os.MkdirAll(path[:len(path)-len(entryID)-1], 0750); err != nil { return err } return os.WriteFile(path, Pack(data), 0640) } func ObjectRead(ctx *AccessContext, dossierID, entryID string) ([]byte, error) { accessorID := "" if ctx != nil { accessorID = ctx.AccessorID } if !CheckAccess(accessorID, dossierID, entryID, PermRead) { return nil, fmt.Errorf("access denied") } packed, err := os.ReadFile(ObjectPath(dossierID, entryID)) if err != nil { return nil, err } return Unpack(packed), nil } func ObjectRemoveByDossier(ctx *AccessContext, dossierID string) error { accessorID := "" if ctx != nil { accessorID = ctx.AccessorID } if !CheckAccess(accessorID, dossierID, "", PermDelete) { return fmt.Errorf("access denied") } return os.RemoveAll(filepath.Join(ObjectDir, dossierID)) } // --- Lab stubs --- func LabEntryListForIndex() ([]*Entry, error) { log.Printf("[STUB] LabEntryListForIndex") return nil, nil } func EntryCategoryCounts(ctx *AccessContext, dossierID string) (map[string]int, error) { accessorID := "" if ctx != nil { accessorID = ctx.AccessorID } entries, err := EntryRead(accessorID, dossierID, &Filter{Category: -1}) if err != nil { return nil, err } counts := map[string]int{} for _, e := range entries { if e.Category > 0 { counts[CategoryName(e.Category)]++ } } return counts, nil } func EntryQueryByDate(dossierID string, from, to int64) ([]*Entry, error) { return EntryRead("", dossierID, &Filter{Category: -1, FromDate: from, ToDate: to}) } // --- Audit types and stubs --- type AuditFilter struct { ActorID string TargetID string Action string FromDate int64 ToDate int64 Limit int } func AuditList(f *AuditFilter) ([]*AuditEntry, error) { if f == nil { return nil, nil } q := "SELECT AuditID, Actor1ID, Actor2ID, TargetID, Action, Details, RelationID, Timestamp FROM audit WHERE 1=1" var args []any if f.TargetID != "" { q += " AND TargetID = ?" args = append(args, f.TargetID) } if f.ActorID != "" { q += " AND Actor1ID = ?" args = append(args, f.ActorID) } if f.FromDate > 0 { q += " AND Timestamp >= ?" args = append(args, f.FromDate) } if f.ToDate > 0 { q += " AND Timestamp < ?" args = append(args, f.ToDate) } q += " ORDER BY Timestamp DESC" if f.Limit > 0 { q += fmt.Sprintf(" LIMIT %d", f.Limit) } var entries []*AuditEntry err := dbQuery(q, args, &entries) return entries, err } // --- Misc ---