Web UI: zone naming and reverse zones, ACL suggestions, record form
- New zone dialog: choose Domain or Reverse zone. Reverse zones are created from a network and their name is generated; both kinds show the exact zone name and flag likely mistakes (addresses or URLs as names, host-like names, duplicates in the view, parent/child zones, networks off the octet/nibble boundaries) before anything is submitted. - Record form: clears every field but the type after an add; shows the record exactly as it will be stored and warns about unqualified names. In reverse zones it takes an address, defaults to PTR, suggests host names of A/AAAA records with that address and warns about a second PTR; the record list shows addresses. - Address match lists (options, view match lists, ACL elements) offer the existing ACLs, TSIG keys and built-ins as one-click suggestions and flag unknown names. - Server: in reverse zones a record can be named by its address, and a PTR target like nas.example.com without the final dot is read as the full host name (a bare label is refused). Warnings noticed while parsing record data reach the result again. - Fixes: brand link hover underline, the dashboard's "New zone" link opening the dialog twice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3b7bee67f8
commit
bf7f99874b
19 files changed
+577
-35
No files matched your search
@@ -450,3 +450,15 @@ func TestZoneCreateWithOptions(t *testing.T) {
|
||||
t.Errorf("primaries option = %+v (the masters synonym should be normalized)", o)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseWarningsReachTheResult(t *testing.T) {
|
||||
env := newEnv(t)
|
||||
env.submit(env.admin, NewRequest(OpZoneCreate, 0, ZoneInput{Name: model.Ptr("10.10.0.0/24")}))
|
||||
res := env.submit(env.admin, NewRequest(OpRecordCreate, 0, RecordInput{Zone: "0.10.10.in-addr.arpa", Name: model.Ptr("10.10.0.20"), Type: model.Ptr("PTR"), Data: model.Ptr("nas.example.com")}))
|
||||
if len(res.Warnings) != 1 || !strings.Contains(res.Warnings[0], "read as the absolute name") {
|
||||
t.Errorf("warnings = %q", res.Warnings)
|
||||
}
|
||||
if len(res.Ops) != 1 || !strings.Contains(res.Ops[0].Summary, "20.0.10.10.in-addr.arpa. IN PTR nas.example.com.") {
|
||||
t.Errorf("ops = %+v", res.Ops)
|
||||
}
|
||||
}
|
||||
@@ -528,6 +528,15 @@ func (x *expander) ptrDelete(z *model.Zone, owner string, p *rr.Parsed) []model.
|
||||
return []model.Op{newOp(OpRecordDelete, 0, 0, in, fmt.Sprintf("delete %s IN PTR %s (if present)", ptrName, owner))}
|
||||
}
|
||||
|
||||
// parseWarnings keeps what parsing noticed (e.g. a name read as absolute).
|
||||
// The stored operation holds the canonical data, so applying it doesn't
|
||||
// notice again.
|
||||
func (x *expander) parseWarnings(owner string, p *rr.Parsed) {
|
||||
for _, w := range p.Warnings {
|
||||
x.warnings = append(x.warnings, fmt.Sprintf("%s %s: %s", dnsname.Display(owner), p.Type, w))
|
||||
}
|
||||
}
|
||||
|
||||
func (x *expander) recordCreate(req Request) ([]model.Op, error) {
|
||||
var in RecordInput
|
||||
if err := decode(req.Data, &in); err != nil {
|
||||
@@ -544,6 +553,7 @@ func (x *expander) recordCreate(req Request) ([]model.Op, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x.parseWarnings(owner, p)
|
||||
ptr := in.PTR
|
||||
in.ZoneID, in.Zone, in.View, in.PTR = z.ID, "", "", false
|
||||
in.Name, in.Type, in.Data = &owner, &p.Type, &p.Data
|
||||
@@ -589,6 +599,7 @@ func (x *expander) recordUpdate(req Request) ([]model.Op, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x.parseWarnings(owner, p)
|
||||
if in.Name != nil {
|
||||
in.Name = &owner
|
||||
}
|
||||
@@ -737,6 +748,7 @@ func (x *expander) rrsetReplace(req Request) ([]model.Op, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x.parseWarnings(owner, p)
|
||||
canon = append(canon, p.Data)
|
||||
}
|
||||
in.ZoneID, in.Zone, in.View, in.Name, in.Type, in.Data = z.ID, "", "", owner, typ, canon
|
||||
|
||||
Reference in new issue
Block a user