inou/lib/roles.go.bak

319 lines
7.8 KiB
Go

package lib
// ============================================================================
// System Role Definitions
// ============================================================================
// Predefined roles that can be applied to grantees. Each role defines
// a set of operations (r/w/d/m) at root level and optionally at specific
// categories.
// ============================================================================
// RoleGrant defines a single permission grant within a role
type RoleGrant struct {
Category int // 0 = root (all categories), >0 = specific category
Ops string // "r", "rw", "rwd", "rwdm"
}
// opsFromString converts string ops ("r", "rw", "rwdm") to int bitmask
func opsFromString(s string) int {
ops := 0
for _, c := range s {
switch c {
case 'r':
ops |= 1
case 'w':
ops |= 2
case 'd':
ops |= 4
case 'm':
ops |= 8
}
}
return ops
}
// RoleTemplate defines a predefined role with its grants
type RoleTemplate struct {
Name string // Role identifier (e.g., "Family", "Doctor")
Description string // Human-readable description
Grants []RoleGrant // Permission grants
}
// SystemRoles defines all available role templates
var SystemRoles = []RoleTemplate{
{
Name: "Parent/Guardian",
Description: "Full access to child's data",
Grants: []RoleGrant{
{Category: 0, Ops: "rwdm"},
},
},
{
Name: "Spouse/Partner",
Description: "Full access to partner's data",
Grants: []RoleGrant{
{Category: 0, Ops: "rwdm"},
},
},
{
Name: "Sibling",
Description: "View and add notes",
Grants: []RoleGrant{
{Category: 0, Ops: "r"},
{Category: CategoryNote, Ops: "rw"},
},
},
{
Name: "Extended Family",
Description: "View only",
Grants: []RoleGrant{
{Category: 0, Ops: "r"},
},
},
{
Name: "Doctor",
Description: "Full clinical access",
Grants: []RoleGrant{
{Category: 0, Ops: "rw"},
},
},
{
Name: "Specialist",
Description: "Clinical data read, write imaging/labs/docs",
Grants: []RoleGrant{
{Category: 0, Ops: "r"},
{Category: CategoryImaging, Ops: "rw"},
{Category: CategoryLab, Ops: "rw"},
{Category: CategoryDocument, Ops: "rw"},
{Category: CategoryAssessment, Ops: "rw"},
},
},
{
Name: "Caregiver",
Description: "Daily care access",
Grants: []RoleGrant{
{Category: 0, Ops: "rw"},
},
},
{
Name: "Physical Therapist",
Description: "Exercise, vitals, imaging",
Grants: []RoleGrant{
{Category: CategoryImaging, Ops: "r"},
{Category: CategoryLab, Ops: "r"},
{Category: CategoryVital, Ops: "rw"},
{Category: CategoryExercise, Ops: "rw"},
{Category: CategoryNote, Ops: "rw"},
},
},
{
Name: "Nutritionist",
Description: "Diet, supplements, labs",
Grants: []RoleGrant{
{Category: CategoryLab, Ops: "r"},
{Category: CategoryVital, Ops: "r"},
{Category: CategoryNutrition, Ops: "rw"},
{Category: CategorySupplement, Ops: "rw"},
},
},
{
Name: "Trainer",
Description: "Exercise, nutrition, vitals",
Grants: []RoleGrant{
{Category: CategoryVital, Ops: "r"},
{Category: CategoryExercise, Ops: "rw"},
{Category: CategoryNutrition, Ops: "rw"},
},
},
{
Name: "Therapist",
Description: "Mental health, notes, assessments",
Grants: []RoleGrant{
{Category: CategoryLab, Ops: "r"},
{Category: CategoryVital, Ops: "r"},
{Category: CategoryNote, Ops: "rw"},
{Category: CategorySymptom, Ops: "rw"},
{Category: CategoryAssessment, Ops: "rw"},
},
},
{
Name: "Pharmacist",
Description: "Medications, labs, genome",
Grants: []RoleGrant{
{Category: CategoryMedication, Ops: "r"},
{Category: CategoryLab, Ops: "r"},
{Category: CategoryGenome, Ops: "r"},
},
},
{
Name: "Friend",
Description: "View only",
Grants: []RoleGrant{
{Category: 0, Ops: "r"},
},
},
{
Name: "Researcher",
Description: "View only for research",
Grants: []RoleGrant{
{Category: 0, Ops: "r"},
},
},
{
Name: "Emergency",
Description: "Emergency read access",
Grants: []RoleGrant{
{Category: 0, Ops: "r"},
},
},
}
// GetSystemRoles returns all system role templates
func GetSystemRoles() []RoleTemplate {
return SystemRoles
}
// GetSystemRole returns a specific role template by name
func GetSystemRole(name string) *RoleTemplate {
for _, r := range SystemRoles {
if r.Name == name {
return &r
}
}
return nil
}
// ApplyRoleTemplate creates access grants for a grantee based on a role template.
// For category-specific grants, creates a root entry for that category in the
// dossier to serve as the grant target.
func ApplyRoleTemplate(dossierID, granteeID, roleName string) error {
role := GetSystemRole(roleName)
if role == nil {
// Unknown role - create basic read-only grant
return AccessGrantWrite(&Access{
DossierID: dossierID,
GranteeID: granteeID,
EntryID: "", // root
Relation: 0,
Ops: 1, // read only
})
}
var grants []*Access
for _, g := range role.Grants {
grant := &Access{
DossierID: dossierID,
GranteeID: granteeID,
Relation: 0, // default relation
Ops: opsFromString(g.Ops),
}
if g.Category == 0 {
// Root grant
grant.EntryID = ""
} else {
// Category-specific grant
// Find or create category root entry
catRootID, err := findOrCreateCategoryRoot(dossierID, g.Category)
if err != nil {
return err
}
grant.EntryID = catRootID
}
grants = append(grants, grant)
}
err := AccessGrantWrite(grants...)
if err != nil {
return err
}
// Invalidate cache for grantee
InvalidateCacheForAccessor(granteeID)
return nil
}
// findOrCreateCategoryRoot is an alias for EnsureCategoryRoot (in access.go)
func findOrCreateCategoryRoot(dossierID string, category int) (string, error) {
return EnsureCategoryRoot(dossierID, category)
}
// RevokeRole removes all grants for a grantee on a dossier
// Note: Role tracking removed from Access - this now removes ALL grants
func RevokeRole(dossierID, granteeID, roleName string) error {
grants, err := AccessGrantList(&PermissionFilter{
DossierID: dossierID,
GranteeID: granteeID,
})
if err != nil {
return err
}
var ids []string
for _, g := range grants {
ids = append(ids, g.AccessID)
}
if len(ids) > 0 {
if err := AccessGrantRemove(ids...); err != nil {
return err
}
}
// Invalidate cache for grantee
InvalidateCacheForAccessor(granteeID)
return nil
}
// GetGranteeRole returns the primary role name for a grantee on a dossier
// Note: Role tracking removed from Access - returns empty string
func GetGranteeRole(dossierID, granteeID string) string {
// Role field no longer tracked in Access struct
return ""
}
// GetGranteesWithAccess returns all grantees with any access to a dossier
// along with their role and ops
type GranteeSummary struct {
GranteeID string
Name string // Resolved from dossier
Role string
Ops string // Combined ops from all grants
}
func GetGranteesWithAccess(dossierID string) ([]GranteeSummary, error) {
grants, err := AccessGrantList(&PermissionFilter{DossierID: dossierID})
if err != nil {
return nil, err
}
// Group by grantee
byGrantee := make(map[string]*GranteeSummary)
for _, g := range grants {
if g.GranteeID == "" {
continue // Skip templates
}
if byGrantee[g.GranteeID] == nil {
byGrantee[g.GranteeID] = &GranteeSummary{
GranteeID: g.GranteeID,
Role: "", // Role field no longer tracked
}
}
// Merge ops (int bitmask, convert to string for display)
merged := mergeOps(opsFromString(byGrantee[g.GranteeID].Ops), g.Ops)
byGrantee[g.GranteeID].Ops = OpsToString(merged)
}
// Resolve names (using nil ctx for internal operation)
var result []GranteeSummary
for _, gs := range byGrantee {
if d, err := DossierGet(nil, gs.GranteeID); err == nil {
gs.Name = d.Name
}
result = append(result, *gs)
}
return result, nil
}