package pvetest import ( "net/http" "sort" "strings" ) // access fakes /access/groups, /access/roles and /access/users (user.cfg). func (s *Server) access(w http.ResponseWriter, r *http.Request, method string, seg []string) { f := r.PostForm if method == http.MethodDelete { f = r.URL.Query() } switch { case len(seg) == 2 && seg[1] == "groups" && method == "GET": var out []any for _, id := range sortedKeys(s.Groups) { o := map[string]any{"groupid": id, "users": strings.Join(s.members(id), ",")} for k, v := range s.Groups[id] { o[k] = v } out = append(out, o) } reply(w, out) case len(seg) == 2 && seg[1] == "groups" && method == "POST": id := f.Get("groupid") if s.Groups[id] != nil { fail(w, 500, "create group failed: group '"+id+"' already exists") return } s.Groups[id] = setFields(map[string]any{}, f, "groupid") reply(w, nil) case len(seg) == 3 && seg[1] == "groups": g := s.Groups[seg[2]] if g == nil { fail(w, 500, "group '"+seg[2]+"' does not exist") return } switch method { case "PUT": setFields(g, f) case "DELETE": delete(s.Groups, seg[2]) for _, u := range s.Users { u["groups"] = strings.Join(remove(split(u["groups"]), seg[2]), ",") } } reply(w, nil) case len(seg) == 2 && seg[1] == "roles" && method == "GET": out := []any{ map[string]any{"roleid": "Administrator", "privs": "Sys.Audit,Sys.Modify", "special": 1}, map[string]any{"roleid": "PVEAuditor", "privs": "Sys.Audit", "special": 1}, } for _, id := range sortedKeys(s.Roles) { out = append(out, map[string]any{"roleid": id, "privs": s.Roles[id], "special": 0}) } reply(w, out) case len(seg) == 2 && seg[1] == "roles" && method == "POST": s.Roles[f.Get("roleid")] = f.Get("privs") reply(w, nil) case len(seg) == 3 && seg[1] == "roles": if _, ok := s.Roles[seg[2]]; !ok { fail(w, 500, "role '"+seg[2]+"' does not exist") return } if method == "DELETE" { delete(s.Roles, seg[2]) } else { s.Roles[seg[2]] = f.Get("privs") } reply(w, nil) case len(seg) == 2 && seg[1] == "users" && method == "GET": var out []any for _, id := range sortedKeys(s.Users) { realm := id[strings.LastIndex(id, "@")+1:] o := map[string]any{"userid": id, "realm-type": realm, "tokens": []any{}} for k, v := range s.Users[id] { o[k] = v } out = append(out, o) } reply(w, out) case len(seg) == 2 && seg[1] == "users" && method == "POST": id := f.Get("userid") if s.Users[id] != nil { fail(w, 500, "create user failed: user '"+id+"' already exists") return } if !s.groupsExist(w, f.Get("groups")) { return } s.Users[id] = setFields(map[string]any{"enable": 1}, f, "userid", "password") reply(w, nil) case len(seg) == 3 && seg[1] == "users": u := s.Users[seg[2]] if u == nil { fail(w, 500, "user '"+seg[2]+"' does not exist") return } switch method { case "PUT": if !s.groupsExist(w, f.Get("groups")) { return } setFields(u, f) case "DELETE": delete(s.Users, seg[2]) delete(s.Shadow, strings.TrimSuffix(seg[2], "@pve")) } reply(w, nil) default: fail(w, 501, "not implemented in fake: "+method+" "+strings.Join(seg, "/")) } } func (s *Server) groupsExist(w http.ResponseWriter, groups string) bool { for _, g := range split(groups) { if s.Groups[g] == nil { fail(w, 500, "no such group '"+g+"'") return false } } return true } func (s *Server) members(group string) []string { var out []string for id, u := range s.Users { for _, g := range split(u["groups"]) { if g == group { out = append(out, id) } } } sort.Strings(out) return out } // setFields copies form values; an empty value removes the field (like user.cfg). func setFields(m map[string]any, f map[string][]string, skip ...string) map[string]any { sk := map[string]bool{} for _, k := range skip { sk[k] = true } for k, v := range f { if sk[k] || len(v) == 0 { continue } if v[0] == "" { delete(m, k) continue } m[k] = v[0] } return m } func split(v any) []string { s, _ := v.(string) var out []string for _, x := range strings.Split(s, ",") { if x = strings.TrimSpace(x); x != "" { out = append(out, x) } } return out } func remove(l []string, x string) []string { out := l[:0] for _, e := range l { if e != x { out = append(out, e) } } return out } func sortedKeys[V any](m map[string]V) []string { out := make([]string, 0, len(m)) for k := range m { out = append(out, k) } sort.Strings(out) return out }