379 lines
9.3 KiB
Go
379 lines
9.3 KiB
Go
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 ---
|
|
|