169 lines
4.2 KiB
Go
169 lines
4.2 KiB
Go
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
|
|
}
|