fix: multi issues - TUN read loop, SDWAN routing for TenantID=0, WS keepalive 10s
This commit is contained in:
185
internal/server/tenant_api.go
Normal file
185
internal/server/tenant_api.go
Normal file
@@ -0,0 +1,185 @@
|
||||
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{}
|
||||
Reference in New Issue
Block a user