diff --git a/policies/rules/security/service.go b/policies/rules/security/service.go index 9b23a34..c05e927 100644 --- a/policies/rules/security/service.go +++ b/policies/rules/security/service.go @@ -68,119 +68,21 @@ func (s *Service) Create(ctx context.Context, loc Location, entry Entry) (*Entry // // Param action should be either "get" or "show". func (s *Service) Read(ctx context.Context, loc Location, name, action string) (*Entry, error) { - if name == "" { - return nil, errors.NameNotSpecifiedError - } - - vn := s.client.Versioning() - _, normalizer, err := Versioning(vn) - if err != nil { - return nil, err - } - - path, err := loc.Xpath(vn, name, "") - if err != nil { - return nil, err - } - - cmd := &xmlapi.Config{ - Action: action, - Xpath: util.AsXpath(path), - Target: s.client.GetTarget(), - } - - if _, _, err = s.client.Communicate(ctx, cmd, true, normalizer); err != nil { - // action=show returns empty config like this - if err.Error() == "No such node" && action == "show" { - return nil, errors.ObjectNotFound() - } - return nil, err - } - - list, err := normalizer.Normalize() - if err != nil { - return nil, err - } else if len(list) != 1 { - return nil, fmt.Errorf("expected to %q 1 entry, got %d", action, len(list)) - } - - return &list[0], nil + return s.doRead(ctx, loc, name, action, true, true) } // ReadById returns the given config object for the given uuid, using the specified action. // // Param action should be either "get" or "show". func (s *Service) ReadById(ctx context.Context, loc Location, uuid, action string) (*Entry, error) { - if uuid == "" { - return nil, errors.UuidNotSpecifiedError - } - - vn := s.client.Versioning() - _, normalizer, err := Versioning(vn) - if err != nil { - return nil, err - } - - path, err := loc.Xpath(vn, "", uuid) - if err != nil { - return nil, err - } - - cmd := &xmlapi.Config{ - Action: action, - Xpath: util.AsXpath(path), - Target: s.client.GetTarget(), - } - - if _, _, err = s.client.Communicate(ctx, cmd, true, normalizer); err != nil { - // action=show returns empty config like this - if err.Error() == "No such node" && action == "show" { - return nil, errors.ObjectNotFound() - } - return nil, err - } - - list, err := normalizer.Normalize() - if err != nil { - return nil, err - } else if len(list) != 1 { - return nil, fmt.Errorf("expected to %q 1 entry, got %d", action, len(list)) - } - - return &list[0], nil + return s.doRead(ctx, loc, uuid, action, false, true) } // ReadFromConfig returns the given config object from the loaded XML config. // // Requires that client.LoadPanosConfig() has been invoked. func (s *Service) ReadFromConfig(ctx context.Context, loc Location, name string) (*Entry, error) { - if name == "" { - return nil, errors.NameNotSpecifiedError - } - - vn := s.client.Versioning() - _, normalizer, err := Versioning(vn) - if err != nil { - return nil, err - } - - path, err := loc.Xpath(vn, name, "") - if err != nil { - return nil, err - } - - if _, err = s.client.ReadFromConfig(ctx, path, true, normalizer); err != nil { - return nil, err - } - - list, err := normalizer.Normalize() - if err != nil { - return nil, err - } else if len(list) != 1 { - return nil, fmt.Errorf("expected to find 1 entry, got %d", len(list)) - } - - return &list[0], nil + return s.doRead(ctx, loc, name, "", true, false) } // ReadFromConfigById returns the given config object for the given UUID from the @@ -188,8 +90,15 @@ func (s *Service) ReadFromConfig(ctx context.Context, loc Location, name string) // // Requires that client.LoadPanosConfig() has been invoked. func (s *Service) ReadFromConfigById(ctx context.Context, loc Location, uuid string) (*Entry, error) { - if uuid == "" { - return nil, errors.NameNotSpecifiedError + return s.doRead(ctx, loc, uuid, "", false, false) +} + +func (s *Service) doRead(ctx context.Context, loc Location, value, action string, byName, fromPanos bool) (*Entry, error) { + if value == "" { + if byName { + return nil, errors.NameNotSpecifiedError + } + return nil, errors.UuidNotSpecifiedError } vn := s.client.Versioning() @@ -198,20 +107,41 @@ func (s *Service) ReadFromConfigById(ctx context.Context, loc Location, uuid str return nil, err } - path, err := loc.Xpath(vn, "", uuid) + var path []string + if byName { + path, err = loc.Xpath(vn, value, "") + } else { + path, err = loc.Xpath(vn, "", value) + } if err != nil { return nil, err } - if _, err = s.client.ReadFromConfig(ctx, path, true, normalizer); err != nil { - return nil, err + if fromPanos { + cmd := &xmlapi.Config{ + Action: action, + Xpath: util.AsXpath(path), + Target: s.client.GetTarget(), + } + + if _, _, err = s.client.Communicate(ctx, cmd, true, normalizer); err != nil { + // action=show returns empty config like this + if err.Error() == "No such node" && action == "show" { + return nil, errors.ObjectNotFound() + } + return nil, err + } + } else { + if _, err = s.client.ReadFromConfig(ctx, path, true, normalizer); err != nil { + return nil, err + } } list, err := normalizer.Normalize() if err != nil { return nil, err } else if len(list) != 1 { - return nil, fmt.Errorf("expected to find 1 entry, got %d", len(list)) + return nil, fmt.Errorf("expected to %q 1 entry, got %d", action, len(list)) } return &list[0], nil @@ -219,80 +149,18 @@ func (s *Service) ReadFromConfigById(ctx context.Context, loc Location, uuid str // Update updates the given config object, then returns the result. func (s *Service) Update(ctx context.Context, loc Location, entry Entry, oldName string) (*Entry, error) { - if entry.Name == "" { - return nil, errors.NameNotSpecifiedError - } - - vn := s.client.Versioning() - updates := xmlapi.NewMultiConfig(2) - specifier, _, err := Versioning(vn) - if err != nil { - return nil, err - } - - // Get the old config. - var old *Entry - if oldName != "" && oldName != entry.Name { - // Action needed: rename. - path, err := loc.Xpath(vn, oldName, "") - if err != nil { - return nil, err - } - - old, err = s.Read(ctx, loc, oldName, "get") - - updates.Add(&xmlapi.Config{ - Action: "rename", - Xpath: util.AsXpath(path), - NewName: entry.Name, - Target: s.client.GetTarget(), - }) - } else { - old, err = s.Read(ctx, loc, entry.Name, "get") - } - if err != nil { - return nil, err - } - - if !SpecMatches(&entry, old) { - // Action needed: edit. - path, err := loc.Xpath(vn, entry.Name, "") - if err != nil { - return nil, err - } - - // Copy over the misc stuff. - entry.CopyMiscFrom(old) - - updateSpec, err := specifier(entry) - if err != nil { - return nil, err - } - - updates.Add(&xmlapi.Config{ - Action: "edit", - Xpath: util.AsXpath(path), - Element: updateSpec, - Target: s.client.GetTarget(), - }) - } - - // Do the updates we've built up. - if len(updates.Operations) != 0 { - if _, _, _, err = s.client.MultiConfig(ctx, updates, false, nil); err != nil { - return nil, err - } - } - - // Return the read results. - return s.Read(ctx, loc, entry.Name, "get") + return s.doUpdate(ctx, loc, entry, oldName, true) } // UpdateById updates the given config object by uuid, then returns the result. func (s *Service) UpdateById(ctx context.Context, loc Location, entry Entry, uuid string) (*Entry, error) { + return s.doUpdate(ctx, loc, entry, uuid, false) +} + +func (s *Service) doUpdate(ctx context.Context, loc Location, entry Entry, value string, byName bool) (*Entry, error) { if entry.Name == "" { return nil, errors.NameNotSpecifiedError - } else if uuid == "" { + } else if !byName && value == "" { return nil, errors.UuidNotSpecifiedError } @@ -303,14 +171,24 @@ func (s *Service) UpdateById(ctx context.Context, loc Location, entry Entry, uui return nil, err } - // Get the old config. - old, err := s.ReadById(ctx, loc, uuid, "get") + var old *Entry + if byName { + if value == "" || value == entry.Name { + old, err = s.Read(ctx, loc, entry.Name, "get") + } else { + old, err = s.Read(ctx, loc, value, "get") + } + } else { + old, err = s.ReadById(ctx, loc, value, "get") + } if err != nil { return nil, err + } else if old == nil { + return nil, fmt.Errorf("previous rule doesn't exist for update") } + // Check for rename. if old.Name != entry.Name { - // Action needed: rename. path, err := loc.Xpath(vn, old.Name, "") if err != nil { return nil, err @@ -324,8 +202,8 @@ func (s *Service) UpdateById(ctx context.Context, loc Location, entry Entry, uui }) } + // Check for edit. if !SpecMatches(&entry, old) { - // Action needed: edit. path, err := loc.Xpath(vn, entry.Name, "") if err != nil { return nil, err @@ -360,37 +238,31 @@ func (s *Service) UpdateById(ctx context.Context, loc Location, entry Entry, uui // Delete deletes the given item. func (s *Service) Delete(ctx context.Context, loc Location, name string) error { - if name == "" { - return errors.NameNotSpecifiedError - } - - vn := s.client.Versioning() - - path, err := loc.Xpath(vn, name, "") - if err != nil { - return err - } - - cmd := &xmlapi.Config{ - Action: "delete", - Xpath: util.AsXpath(path), - Target: s.client.GetTarget(), - } - - _, _, err = s.client.Communicate(ctx, cmd, false, nil) - - return err + return s.doDelete(ctx, loc, name, true) } // DeleteById deletes the given item using the uuid. func (s *Service) DeleteById(ctx context.Context, loc Location, uuid string) error { - if uuid == "" { + return s.doDelete(ctx, loc, uuid, false) +} + +func (s *Service) doDelete(ctx context.Context, loc Location, value string, byName bool) error { + if value == "" { + if byName { + return errors.NameNotSpecifiedError + } return errors.UuidNotSpecifiedError } vn := s.client.Versioning() - path, err := loc.Xpath(vn, "", uuid) + var path []string + var err error + if byName { + path, err = loc.Xpath(vn, value, "") + } else { + path, err = loc.Xpath(vn, "", value) + } if err != nil { return err } @@ -412,59 +284,7 @@ func (s *Service) DeleteById(ctx context.Context, loc Location, uuid string) err // // Params filter and quote are for client side filtering. func (s *Service) List(ctx context.Context, loc Location, action, filter, quote string) ([]Entry, error) { - var err error - - var logic *filtering.Group - if filter != "" { - logic, err = filtering.Parse(filter, quote) - if err != nil { - return nil, err - } - } - - vn := s.client.Versioning() - - _, normalizer, err := Versioning(vn) - if err != nil { - return nil, err - } - - path, err := loc.Xpath(vn, "", "") - if err != nil { - return nil, err - } - - cmd := &xmlapi.Config{ - Action: action, - Xpath: util.AsXpath(path), - Target: s.client.GetTarget(), - } - - if _, _, err = s.client.Communicate(ctx, cmd, true, normalizer); err != nil { - // action=show returns empty config like this, it is not an error. - if err.Error() == "No such node" && action == "show" { - return nil, nil - } - return nil, err - } - - listing, err := normalizer.Normalize() - if err != nil || logic == nil { - return listing, err - } - - filtered := make([]Entry, 0, len(listing)) - for _, x := range listing { - ok, err := logic.Matches(&x) - if err != nil { - return nil, err - } - if ok { - filtered = append(filtered, x) - } - } - - return filtered, nil + return s.doList(ctx, loc, action, filter, quote, true) } // ListFromConfig returns a list of objects at the given location. @@ -473,14 +293,13 @@ func (s *Service) List(ctx context.Context, loc Location, action, filter, quote // // Params filter and quote are for client side filtering. func (s *Service) ListFromConfig(ctx context.Context, loc Location, filter, quote string) ([]Entry, error) { - var err error + return s.doList(ctx, loc, "", filter, quote, false) +} - var logic *filtering.Group - if filter != "" { - logic, err = filtering.Parse(filter, quote) - if err != nil { - return nil, err - } +func (s *Service) doList(ctx context.Context, loc Location, action, filter, quote string, fromPanos bool) ([]Entry, error) { + logic, err := filtering.Parse(filter, quote) + if err != nil { + return nil, err } vn := s.client.Versioning() @@ -494,10 +313,27 @@ func (s *Service) ListFromConfig(ctx context.Context, loc Location, filter, quot if err != nil { return nil, err } - path = path[:len(path)-1] - if _, err = s.client.ReadFromConfig(ctx, path, false, normalizer); err != nil { - return nil, err + if fromPanos { + cmd := &xmlapi.Config{ + Action: action, + Xpath: util.AsXpath(path), + Target: s.client.GetTarget(), + } + + if _, _, err = s.client.Communicate(ctx, cmd, true, normalizer); err != nil { + // action=show returns empty config like this, it is not an error. + if err.Error() == "No such node" && action == "show" { + return nil, nil + } + return nil, err + } + } else { + path = path[:len(path)-1] + + if _, err = s.client.ReadFromConfig(ctx, path, false, normalizer); err != nil { + return nil, err + } } listing, err := normalizer.Normalize()