package clibouncer import ( "context" "errors" "fmt" "strings" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args" "github.com/crowdsecurity/crowdsec/pkg/database/ent" "github.com/crowdsecurity/crowdsec/pkg/types" ) func (cli *cliBouncers) findParentBouncer(bouncerName string, bouncers []*ent.Bouncer) (string, error) { bouncerPrefix := strings.Split(bouncerName, "@")[0] for _, bouncer := range bouncers { if strings.HasPrefix(bouncer.Name, bouncerPrefix) && !bouncer.AutoCreated { return bouncer.Name, nil } } return "", errors.New("no parent bouncer found") } func (cli *cliBouncers) delete(ctx context.Context, bouncers []string, ignoreMissing bool) error { allBouncers, err := cli.db.ListBouncers(ctx) if err != nil { return fmt.Errorf("unable to list bouncers: %w", err) } for _, bouncerName := range bouncers { bouncer, err := cli.db.SelectBouncerByName(ctx, bouncerName) if err != nil { var notFoundErr *ent.NotFoundError if ignoreMissing && errors.As(err, ¬FoundErr) { continue } return fmt.Errorf("unable to delete bouncer %s: %w", bouncerName, err) } // For TLS bouncers, always delete them, they have no parents if bouncer.AuthType == types.TlsAuthType { if err := cli.db.DeleteBouncer(ctx, bouncerName); err != nil { return fmt.Errorf("unable to delete bouncer %s: %w", bouncerName, err) } continue } if bouncer.AutoCreated { parentBouncer, err := cli.findParentBouncer(bouncerName, allBouncers) if err != nil { log.Errorf("bouncer '%s' is auto-created, but couldn't find a parent bouncer", err) continue } log.Warnf("bouncer '%s' is auto-created and cannot be deleted, delete parent bouncer %s instead", bouncerName, parentBouncer) continue } //Try to find all child bouncers and delete them for _, childBouncer := range allBouncers { if strings.HasPrefix(childBouncer.Name, bouncerName+"@") && childBouncer.AutoCreated { if err := cli.db.DeleteBouncer(ctx, childBouncer.Name); err != nil { return fmt.Errorf("unable to delete bouncer %s: %w", childBouncer.Name, err) } log.Infof("bouncer '%s' deleted successfully", childBouncer.Name) } } if err := cli.db.DeleteBouncer(ctx, bouncerName); err != nil { return fmt.Errorf("unable to delete bouncer %s: %w", bouncerName, err) } log.Infof("bouncer '%s' deleted successfully", bouncerName) } return nil } func (cli *cliBouncers) newDeleteCmd() *cobra.Command { var ignoreMissing bool cmd := &cobra.Command{ Use: "delete MyBouncerName", Short: "delete bouncer(s) from the database", Example: `cscli bouncers delete "bouncer1" "bouncer2"`, Args: args.MinimumNArgs(1), Aliases: []string{"remove"}, DisableAutoGenTag: true, ValidArgsFunction: cli.validBouncerID, RunE: func(cmd *cobra.Command, args []string) error { return cli.delete(cmd.Context(), args, ignoreMissing) }, } flags := cmd.Flags() flags.BoolVar(&ignoreMissing, "ignore-missing", false, "don't print errors if one or more bouncers don't exist") return cmd }