package main import ( "encoding/json" "net/http" "strings" "inou/lib" ) type AccessRecord struct { Accessor string `json:"accessor"` Target string `json:"target"` Name string `json:"name,omitempty"` Relation int `json:"relation"` IsCareReceiver bool `json:"is_care_receiver"` CanEdit bool `json:"can_edit"` CreatedAt int64 `json:"created_at,omitempty"` AccessedAt int64 `json:"accessed_at,omitempty"` } type AccessRequest struct { Accessor string `json:"accessor"` Target string `json:"target"` Relation int `json:"relation"` IsCareReceiver bool `json:"is_care_receiver"` CanEdit bool `json:"can_edit"` Delete bool `json:"delete"` Touch bool `json:"touch"` } func handleAccess(w http.ResponseWriter, r *http.Request) { if !isLocalhost(r) { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } if r.Method == "GET" { handleAccessGet(w, r) return } if r.Method == "POST" { handleAccessPost(w, r) return } http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) } func handleAccessGet(w http.ResponseWriter, r *http.Request) { accessorHex := r.URL.Query().Get("accessor") targetHex := r.URL.Query().Get("target") w.Header().Set("Content-Type", "application/json") // Single record lookup if accessorHex != "" && targetHex != "" { access, err := lib.AccessGet(accessorHex, targetHex) if err != nil || access == nil { json.NewEncoder(w).Encode(map[string]interface{}{"found": false}) return } json.NewEncoder(w).Encode(AccessRecord{ Accessor: accessorHex, Target: targetHex, Relation: 0, // Relation removed from RBAC IsCareReceiver: false, // deprecated field CanEdit: (access.Ops & lib.PermWrite) != 0, CreatedAt: access.CreatedAt, AccessedAt: access.CreatedAt, // Use CreatedAt as fallback }) return } // List all with access to target (includes names from dossiers join) if targetHex != "" { records, err := lib.AccessListByTargetWithNames(targetHex) if err != nil { json.NewEncoder(w).Encode([]interface{}{}) return } var result []AccessRecord for _, rec := range records { result = append(result, AccessRecord{ Accessor: rec["accessor_id"].(string), Target: targetHex, Name: rec["name"].(string), Relation: rec["relation"].(int), IsCareReceiver: rec["is_care_receiver"].(bool), CanEdit: rec["can_edit"].(bool), }) } json.NewEncoder(w).Encode(result) return } http.Error(w, "target required", http.StatusBadRequest) } func handleAccessPost(w http.ResponseWriter, r *http.Request) { var req AccessRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid JSON", http.StatusBadRequest) return } if req.Accessor == "" || req.Target == "" { http.Error(w, "accessor and target required", http.StatusBadRequest) return } accessorID := req.Accessor targetID := req.Target w.Header().Set("Content-Type", "application/json") if req.Delete { err := lib.AccessRemove(accessorID, targetID) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } json.NewEncoder(w).Encode(map[string]interface{}{"deleted": true}) return } if req.Touch { err := lib.AccessUpdateTimestamp(accessorID, targetID) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } json.NewEncoder(w).Encode(map[string]interface{}{"touched": true}) return } // Grant access using new RBAC system ops := lib.PermRead if req.CanEdit { ops |= lib.PermWrite } if err := lib.GrantAccess(targetID, accessorID, targetID, ops, req.Relation); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } json.NewEncoder(w).Encode(AccessRecord{ Accessor: req.Accessor, Target: req.Target, Relation: req.Relation, IsCareReceiver: req.IsCareReceiver, CanEdit: req.CanEdit, }) } func isLocalhost(r *http.Request) bool { ip := r.RemoteAddr return strings.HasPrefix(ip, "127.0.0.1") || strings.HasPrefix(ip, "[::1]") } func boolToInt(b bool) int { if b { return 1 } return 0 }