package server import ( "encoding/json" "fmt" "io" "net/http" "strings" "time" "github.com/openp2p-cn/inp2p/internal/store" ) // helpers func BearerToken(r *http.Request) string { h := r.Header.Get("Authorization") if h == "" { return "" } parts := strings.SplitN(h, " ", 2) if len(parts) != 2 { return "" } if strings.ToLower(parts[0]) != "bearer" { return "" } return strings.TrimSpace(parts[1]) } func writeJSON(w http.ResponseWriter, status int, body string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) io.WriteString(w, body) } func (s *Server) HandleAdminCreateTenant(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { writeJSON(w, http.StatusMethodNotAllowed, `{"error":1,"message":"method not allowed"}`) return } var req struct { Name string `json:"name"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.Name == "" { writeJSON(w, http.StatusBadRequest, `{"error":1,"message":"bad request"}`) return } if s.store == nil { writeJSON(w, http.StatusInternalServerError, `{"error":1,"message":"store not ready"}`) return } ten, err := s.store.CreateTenant(req.Name) if err != nil { writeJSON(w, http.StatusInternalServerError, `{"error":1,"message":"create tenant failed"}`) return } resp := struct { Error int `json:"error"` Message string `json:"message"` Tenant int64 `json:"tenant_id"` Subnet string `json:"subnet"` }{0, "ok", ten.ID, ten.Subnet} b, _ := json.Marshal(resp) writeJSON(w, http.StatusOK, string(b)) } func (s *Server) HandleAdminCreateAPIKey(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { writeJSON(w, http.StatusMethodNotAllowed, `{"error":1,"message":"method not allowed"}`) return } if s.store == nil { writeJSON(w, http.StatusInternalServerError, `{"error":1,"message":"store not ready"}`) return } // /api/v1/admin/tenants/{id}/keys parts := strings.Split(strings.Trim(r.URL.Path, "/"), "/") if len(parts) < 6 || parts[5] != "keys" { writeJSON(w, http.StatusBadRequest, `{"error":1,"message":"bad request"}`) return } // parts: api v1 admin tenants {id} keys idPart := parts[4] var tenantID int64 _, _ = fmt.Sscanf(idPart, "%d", &tenantID) if tenantID == 0 { writeJSON(w, http.StatusBadRequest, `{"error":1,"message":"bad request"}`) return } var req struct { Scope string `json:"scope"` TTL int64 `json:"ttl"` // seconds } _ = json.NewDecoder(r.Body).Decode(&req) var ttl time.Duration if req.TTL > 0 { ttl = time.Duration(req.TTL) * time.Second } key, err := s.store.CreateAPIKey(tenantID, req.Scope, ttl) if err != nil { writeJSON(w, http.StatusInternalServerError, `{"error":1,"message":"create key failed"}`) return } resp := struct { Error int `json:"error"` Message string `json:"message"` APIKey string `json:"api_key"` Tenant int64 `json:"tenant_id"` }{0, "ok", key, tenantID} b, _ := json.Marshal(resp) writeJSON(w, http.StatusOK, string(b)) } func (s *Server) HandleTenantEnroll(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { writeJSON(w, http.StatusMethodNotAllowed, `{"error":1,"message":"method not allowed"}`) return } // tenant auth by API key if s.store == nil { writeJSON(w, http.StatusInternalServerError, `{"error":1,"message":"store not ready"}`) return } tok := BearerToken(r) ten, err := s.store.VerifyAPIKey(tok) if err != nil || ten == nil { writeJSON(w, http.StatusUnauthorized, `{"error":1,"message":"unauthorized"}`) return } code, err := s.store.CreateEnrollToken(ten.ID, 10*time.Minute, 5) if err != nil { writeJSON(w, http.StatusInternalServerError, `{"error":1,"message":"create enroll failed"}`) return } resp := struct { Error int `json:"error"` Message string `json:"message"` Code string `json:"enroll_code"` Tenant int64 `json:"tenant_id"` }{0, "ok", code, ten.ID} b, _ := json.Marshal(resp) writeJSON(w, http.StatusOK, string(b)) } func (s *Server) HandleEnrollConsume(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { writeJSON(w, http.StatusMethodNotAllowed, `{"error":1,"message":"method not allowed"}`) return } var req struct { Code string `json:"code"` NodeName string `json:"node"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.Code == "" || req.NodeName == "" { writeJSON(w, http.StatusBadRequest, `{"error":1,"message":"bad request"}`) return } if s.store == nil { writeJSON(w, http.StatusInternalServerError, `{"error":1,"message":"store not ready"}`) return } et, err := s.store.ConsumeEnrollToken(req.Code) if err != nil { s.store.IncEnrollAttempt(req.Code) writeJSON(w, http.StatusUnauthorized, `{"error":1,"message":"invalid enroll"}`) return } cred, err := s.store.CreateNodeCredential(et.TenantID, req.NodeName) if err != nil { writeJSON(w, http.StatusInternalServerError, `{"error":1,"message":"create node failed"}`) return } resp := struct { Error int `json:"error"` Message string `json:"message"` NodeID int64 `json:"node_id"` Secret string `json:"node_secret"` Tenant int64 `json:"tenant_id"` }{0, "ok", cred.NodeID, cred.Secret, cred.TenantID} b, _ := json.Marshal(resp) writeJSON(w, http.StatusOK, string(b)) } // placeholder to avoid unused import var _ = store.Tenant{}