web: restore full single-file console and fix enroll revoke route
This commit is contained in:
@@ -106,6 +106,17 @@ func main() {
|
||||
fmt.Fprintf(w, `{"error":401,"message":"unauthorized"}`)
|
||||
return
|
||||
}
|
||||
// RBAC: admin only
|
||||
if srv.Store() != nil {
|
||||
if u, err := srv.Store().GetUserByToken(server.BearerToken(r)); err == nil && u != nil {
|
||||
if u.Status != 1 || u.Role != "admin" {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
fmt.Fprintf(w, `{"error":403,"message":"forbidden"}`)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
next(w, r)
|
||||
}
|
||||
}
|
||||
@@ -122,9 +133,27 @@ func main() {
|
||||
next(w, r)
|
||||
return
|
||||
}
|
||||
// check API key
|
||||
// check API key + RBAC
|
||||
if srv.Store() != nil {
|
||||
if ten, err := srv.Store().VerifyAPIKey(server.BearerToken(r)); err == nil && ten != nil {
|
||||
// role check if user exists
|
||||
if u, err := srv.Store().GetUserByToken(server.BearerToken(r)); err == nil && u != nil {
|
||||
if u.Status != 1 {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
fmt.Fprintf(w, `{"error":403,"message":"forbidden"}`)
|
||||
return
|
||||
}
|
||||
if u.Role == "operator" {
|
||||
path := r.URL.Path
|
||||
if path != "/api/v1/nodes" && path != "/api/v1/sdwans" && path != "/api/v1/sdwan/edit" && path != "/api/v1/connect" && path != "/api/v1/nodes/apps" && path != "/api/v1/nodes/kick" && path != "/api/v1/stats" && path != "/api/v1/health" {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
fmt.Fprintf(w, `{"error":403,"message":"forbidden"}`)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
next(w, r)
|
||||
return
|
||||
}
|
||||
@@ -146,36 +175,88 @@ func main() {
|
||||
// Tenant APIs (API key auth inside handlers)
|
||||
mux.HandleFunc("/api/v1/admin/tenants", adminMiddleware(srv.HandleAdminCreateTenant))
|
||||
mux.HandleFunc("/api/v1/admin/tenants/", adminMiddleware(srv.HandleAdminCreateAPIKey))
|
||||
mux.HandleFunc("/api/v1/admin/users", adminMiddleware(srv.HandleAdminUsers))
|
||||
mux.HandleFunc("/api/v1/admin/users/", adminMiddleware(srv.HandleAdminUsers))
|
||||
mux.HandleFunc("/api/v1/tenants/enroll", srv.HandleTenantEnroll)
|
||||
mux.HandleFunc("/api/v1/enroll/consume", srv.HandleEnrollConsume)
|
||||
mux.HandleFunc("/api/v1/enroll/consume/", srv.HandleEnrollConsume)
|
||||
|
||||
mux.HandleFunc("/api/v1/auth/login", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
var req struct {
|
||||
Token uint64 `json:"token,string"` // support string from frontend
|
||||
// Support two modes:
|
||||
// 1) token login: {"token":"xxxx"} (admin/master only, backward compatible)
|
||||
// 2) user login: {"tenant":1,"username":"admin","password":"pass"}
|
||||
var reqTok struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
var reqUser struct {
|
||||
TenantID int64 `json:"tenant"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
// Try string first then uint64
|
||||
body, _ := io.ReadAll(r.Body)
|
||||
if err := json.Unmarshal(body, &req); err != nil {
|
||||
var req2 struct {
|
||||
Token uint64 `json:"token"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &req2); err != nil {
|
||||
http.Error(w, "bad request", http.StatusBadRequest)
|
||||
_ = json.Unmarshal(body, &reqTok)
|
||||
_ = json.Unmarshal(body, &reqUser)
|
||||
|
||||
// --- user login ---
|
||||
if reqUser.TenantID > 0 && reqUser.Username != "" && reqUser.Password != "" {
|
||||
if srv.Store() == nil {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
fmt.Fprintf(w, `{"error":1,"message":"store not ready"}`)
|
||||
return
|
||||
}
|
||||
req.Token = req2.Token
|
||||
u, err := srv.Store().VerifyUserPassword(reqUser.TenantID, reqUser.Username, reqUser.Password)
|
||||
if err != nil || u == nil || u.Status != 1 {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
fmt.Fprintf(w, `{"error":1,"message":"invalid credentials"}`)
|
||||
return
|
||||
}
|
||||
// issue API key for this tenant and return subnet
|
||||
key, err := srv.Store().CreateAPIKey(reqUser.TenantID, "all", 0)
|
||||
if err != nil {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
fmt.Fprintf(w, `{"error":1,"message":"create token failed"}`)
|
||||
return
|
||||
}
|
||||
ten, _ := srv.Store().GetTenantByID(reqUser.TenantID)
|
||||
resp := struct {
|
||||
Error int `json:"error"`
|
||||
Message string `json:"message"`
|
||||
Token string `json:"token"`
|
||||
Role string `json:"role"`
|
||||
Status int `json:"status"`
|
||||
Subnet string `json:"subnet"`
|
||||
}{0, "ok", key, u.Role, u.Status, ""}
|
||||
if ten != nil {
|
||||
resp.Subnet = ten.Subnet
|
||||
}
|
||||
b, _ := json.Marshal(resp)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(b)
|
||||
return
|
||||
}
|
||||
|
||||
valid := req.Token == cfg.Token
|
||||
if !valid {
|
||||
for _, t := range cfg.Tokens {
|
||||
if req.Token == t {
|
||||
valid = true
|
||||
break
|
||||
// --- token login (legacy/admin) ---
|
||||
valid := false
|
||||
role := "admin"
|
||||
status := 1
|
||||
if reqTok.Token != "" {
|
||||
// support numeric token as string
|
||||
if reqTok.Token == fmt.Sprintf("%d", cfg.Token) {
|
||||
valid = true
|
||||
} else {
|
||||
for _, t := range cfg.Tokens {
|
||||
if reqTok.Token == fmt.Sprintf("%d", t) {
|
||||
valid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,8 +266,16 @@ func main() {
|
||||
fmt.Fprintf(w, `{"error":1,"message":"invalid token"}`)
|
||||
return
|
||||
}
|
||||
if srv.Store() != nil {
|
||||
if u, err := srv.Store().GetUserByTenant(0); err == nil && u != nil {
|
||||
if u.Role != "" {
|
||||
role = u.Role
|
||||
}
|
||||
status = u.Status
|
||||
}
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, `{"error":0,"token":"%d"}`, cfg.Token)
|
||||
fmt.Fprintf(w, `{"error":0,"token":"%d","role":"%s","status":%d}`, cfg.Token, role, status)
|
||||
})
|
||||
|
||||
mux.HandleFunc("/api/v1/health", tenantMiddleware(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
Reference in New Issue
Block a user