diff --git a/lib/access.go b/lib/access.go index 66bfe5a..b2ba8a1 100644 --- a/lib/access.go +++ b/lib/access.go @@ -309,6 +309,36 @@ func accessGrantListRaw(f *PermissionFilter) ([]*Access, error) { // Utility Functions // ============================================================================ +// EnsureCategoryEntry creates a category entry if it doesn't exist +// Returns the entry_id of the category entry +func EnsureCategoryEntry(dossierID string, category int) (string, error) { + // Check if category entry already exists + entries, err := EntryList(SystemContext, "", category, &EntryFilter{ + DossierID: dossierID, + Type: "category", + Limit: 1, + }) + if err != nil { + return "", err + } + if len(entries) > 0 { + return entries[0].EntryID, nil + } + + // Create category entry + entry := &Entry{ + DossierID: dossierID, + Category: category, + Type: "category", + Value: CategoryName(category), + ParentID: "", // Categories are root-level + } + if err := EntrySave(SystemContext, entry); err != nil { + return "", err + } + return entry.EntryID, nil +} + // CanAccessDossier returns true if accessor can read dossier (for quick checks) func CanAccessDossier(ctx *AccessContext, dossierID string) bool { return CheckAccess(ctx, dossierID, "", 'r') == nil @@ -319,6 +349,38 @@ func CanManageDossier(ctx *AccessContext, dossierID string) bool { return CheckAccess(ctx, dossierID, "", 'm') == nil } +// GrantAccess creates an access grant +// If entryID is empty, grants root-level access +// If entryID is a category, ensures category entry exists first +func GrantAccess(dossierID, granteeID, entryID, ops string) error { + grant := &Access{ + DossierID: dossierID, + GranteeID: granteeID, + EntryID: entryID, + Ops: ops, + CreatedAt: time.Now().Unix(), + } + err := Save("access", grant) + if err == nil { + InvalidateCacheForAccessor(granteeID) + } + return err +} + +// RevokeAccess removes an access grant +func RevokeAccess(accessID string) error { + // Get the grant to know which accessor to invalidate + var grant Access + if err := Load("access", accessID, &grant); err != nil { + return err + } + err := Delete("access", "access_id", accessID) + if err == nil { + InvalidateCacheForAccessor(grant.GranteeID) + } + return err +} + // GetAccessorOps returns the operations accessor can perform on dossier/entry // Returns empty string if no access func GetAccessorOps(ctx *AccessContext, dossierID, entryID string) string {