Full web : fichiers restants
This commit is contained in:
1 parent
ec3b7c9a01
commit
4826fa5b82
26 files changed
+2537
-793
No files matched your search
@@ -1,7 +1,9 @@
|
||||
// Package pvetest is an in-memory fake of the subset of the PVE 9.x API used by
|
||||
// pve-confsync, for tests. It mimics the behaviours that matter for the engine:
|
||||
// parsed datacenter options on GET, property strings on PUT, SDN pending/running
|
||||
// configurations, global SDN lock with token, rollback and reload task.
|
||||
// pve-confsync, for tests and the demo. It mimics the behaviours that matter for
|
||||
// the engine: ticket login of root@pam, parsed datacenter options on GET,
|
||||
// property strings on PUT, SDN pending/running configurations, global SDN lock
|
||||
// with token, rollback and reload task, groups, roles and accounts of user.cfg,
|
||||
// and an SSH server serving /etc/pve/priv/shadow.cfg (see ssh.go).
|
||||
package pvetest
|
||||
|
||||
import (
|
||||
@@ -43,29 +45,47 @@ func (s SDN) clone() SDN {
|
||||
return c
|
||||
}
|
||||
|
||||
// Password of root@pam on the fake clusters (API and SSH).
|
||||
const Password = "root-secret"
|
||||
|
||||
// Server is a fake PVE cluster.
|
||||
type Server struct {
|
||||
*httptest.Server
|
||||
mu sync.Mutex
|
||||
Version string // pve-manager version, e.g. 9.1.4
|
||||
Options map[string]string // datacenter.cfg as written (property strings)
|
||||
Pending SDN
|
||||
Running SDN
|
||||
Lock string
|
||||
Perms map[string]map[string]any
|
||||
FailOn string // "METHOD path" answering 500, to test rollbacks
|
||||
Calls []string // "METHOD path" of every write
|
||||
locks int
|
||||
mu sync.Mutex
|
||||
Version string // pve-manager version, e.g. 9.1.4
|
||||
Password string // root@pam password
|
||||
Options map[string]string // datacenter.cfg as written (property strings)
|
||||
Pending SDN
|
||||
Running SDN
|
||||
Lock string
|
||||
Groups map[string]map[string]any // groupid -> fields (comment)
|
||||
Roles map[string]string // custom roles: roleid -> privs
|
||||
Users map[string]map[string]any // userid -> fields (groups as "a,b")
|
||||
Shadow map[string]string // user without realm -> hash (priv/shadow.cfg)
|
||||
FailOn string // "METHOD path" answering 500, to test rollbacks
|
||||
Calls []string // "METHOD path" of every write, "SSH write" for shadow.cfg
|
||||
locks int
|
||||
ssh *sshServer
|
||||
}
|
||||
|
||||
// New starts a fake cluster.
|
||||
const ticket, csrf = "PVE:root@pam:FAKE::sig", "fake-csrf"
|
||||
|
||||
// New starts a fake cluster (API in HTTPS and SSH).
|
||||
func New(version string) *Server {
|
||||
s := &Server{Version: version, Options: map[string]string{}, Pending: newSDN(), Running: newSDN(),
|
||||
Perms: map[string]map[string]any{}}
|
||||
s := &Server{Version: version, Password: Password, Options: map[string]string{}, Pending: newSDN(), Running: newSDN(),
|
||||
Groups: map[string]map[string]any{}, Roles: map[string]string{}, Shadow: map[string]string{},
|
||||
Users: map[string]map[string]any{"root@pam": {"enable": 1, "email": "root@localhost"}}}
|
||||
s.Server = httptest.NewTLSServer(http.HandlerFunc(s.handle))
|
||||
s.ssh = startSSH(s)
|
||||
return s
|
||||
}
|
||||
|
||||
// Close stops the API and the SSH server.
|
||||
func (s *Server) Close() {
|
||||
s.ssh.close()
|
||||
s.Server.Close()
|
||||
}
|
||||
|
||||
// Commit makes the pending SDN configuration the running one (like an apply).
|
||||
func (s *Server) Commit() {
|
||||
s.mu.Lock()
|
||||
@@ -87,12 +107,24 @@ func fail(w http.ResponseWriter, code int, msg string) {
|
||||
func (s *Server) handle(w http.ResponseWriter, r *http.Request) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if !strings.HasPrefix(r.Header.Get("Authorization"), "PVEAPIToken=") {
|
||||
_ = r.ParseForm()
|
||||
p := strings.TrimPrefix(r.URL.Path, "/api2/json/")
|
||||
if r.Method == http.MethodPost && p == "access/ticket" {
|
||||
if r.PostForm.Get("username") != "root@pam" || r.PostForm.Get("password") != s.Password {
|
||||
fail(w, 401, "authentication failure")
|
||||
return
|
||||
}
|
||||
reply(w, map[string]any{"ticket": ticket, "CSRFPreventionToken": csrf, "username": "root@pam"})
|
||||
return
|
||||
}
|
||||
if c, err := r.Cookie("PVEAuthCookie"); err != nil || c.Value != ticket {
|
||||
fail(w, 401, "no ticket")
|
||||
return
|
||||
}
|
||||
_ = r.ParseForm()
|
||||
p := strings.TrimPrefix(r.URL.Path, "/api2/json/")
|
||||
if r.Method != http.MethodGet && r.Header.Get("CSRFPreventionToken") != csrf {
|
||||
fail(w, 401, "Permission denied - invalid csrf token")
|
||||
return
|
||||
}
|
||||
seg := strings.Split(p, "/")
|
||||
key := r.Method + " " + p
|
||||
if r.Method != http.MethodGet {
|
||||
@@ -130,8 +162,8 @@ func (s *Server) handle(w http.ResponseWriter, r *http.Request) {
|
||||
reply(w, out)
|
||||
case r.Method == "GET" && len(seg) == 5 && seg[0] == "nodes" && seg[2] == "tasks" && seg[4] == "status":
|
||||
reply(w, map[string]any{"status": "stopped", "exitstatus": "OK"})
|
||||
case key == "GET access/permissions":
|
||||
reply(w, s.Perms)
|
||||
case strings.HasPrefix(p, "access/"):
|
||||
s.access(w, r, r.Method, seg)
|
||||
case key == "GET cluster/options":
|
||||
reply(w, s.parsedOptions())
|
||||
case key == "PUT cluster/options":
|
||||
|
||||
Reference in new issue
Block a user