package server import ( "net/http" "strings" "jarl/internal/auth" "jarl/internal/engine" "jarl/internal/model" ) // zoneCompare: GET /zones/{zone}/compare compares a zone with the same // zone in other views (docs/views.md#comparing-views), from the zone's own // view: ?with=a,b picks the other views (default: every view with a // primary zone of that name), ?name=www[&type=A] narrows it to one name, // ?all=1 includes what is the same. A zone named without its view is // compared from the first view that has it. (?views=a,b, the views to // compare with the first as the base, works too.) func (s *Server) zoneCompare(w http.ResponseWriter, r *http.Request, p *auth.Principal) error { st, err := s.state(r) if err != nil { return err } q := r.URL.Query() list := func(k string) []string { var out []string for _, v := range strings.Split(q.Get(k), ",") { if v = strings.TrimSpace(v); v != "" { out = append(out, v) } } return out } with, views := list("with"), list("views") z, err := s.zoneByPath(r, st, p) if err != nil { // A name in several views is what there is to compare. ref := r.PathValue("zone") if ref == "root" { ref = "." } name, perr := engine.ParseZoneName(ref) zs := st.ZonesNamed(name) if perr != nil || len(zs) < 2 { return err } st.SortByView(zs) z = zs[0] if len(views) > 0 { for _, o := range zs { if strings.EqualFold(st.ViewName(o), views[0]) { z = o } } } } readable := func(o *model.Zone) bool { vn := st.ViewName(o) return o.Type == model.ZonePrimary && p.CanSeeZone(o.Name, vn) && p.Can(auth.Action{Resource: auth.ResRecords, Zone: o.Name, View: vn}) } if !readable(z) { return badRequest("zone %s isn't a primary zone whose records you may read", engine.Display(z.Name)) } zones := []*model.Zone{z} var others []*model.Zone for _, o := range st.ZonesNamed(z.Name) { vn := st.ViewName(o) if o.ID == z.ID || !readable(o) { continue } if len(with) > 0 && !containsFold(with, vn) || len(views) > 0 && !containsFold(views, vn) { continue } others = append(others, o) } st.SortByView(others) zones = append(zones, others...) if len(zones) < 2 { return badRequest("zone %s is in only one view (that you may read): there is nothing to compare", engine.Display(z.Name)) } writeJSON(w, http.StatusOK, engine.Compare(st, zones, engine.CompareFilter{Name: q.Get("name"), Type: q.Get("type"), All: queryBool(r, "all")})) return nil } func containsFold(list []string, s string) bool { for _, x := range list { if strings.EqualFold(x, s) { return true } } return false }