From 1e4d4b4e2d89fd6adbb27327ac955fe901d549f2 Mon Sep 17 00:00:00 2001 From: Cyril David Date: Fri, 14 May 2021 17:51:43 -0700 Subject: [PATCH] feat: auth0 ips { check | unblock } (#297) * auth0 ips: add check / unblock commands == Description This adds the `auth0 ips` command which supports two operations: - auth0 ips check - auth0 ips unblock --- internal/auth/auth.go | 1 + internal/auth/auth_test.go | 1 + internal/auth0/anomaly.go | 17 ++++++ internal/auth0/auth0.go | 4 +- internal/cli/ips.go | 109 +++++++++++++++++++++++++++++++++++++ internal/cli/root.go | 1 + 6 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 internal/auth0/anomaly.go create mode 100644 internal/cli/ips.go diff --git a/internal/auth/auth.go b/internal/auth/auth.go index b99364dd3..18abad422 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -35,6 +35,7 @@ var requiredScopes = []string{ "read:branding", "update:branding", "read:connections", "update:connections", "read:client_keys", "read:logs", "read:tenant_settings", "read:custom_domains", + "read:anomaly_blocks", "delete:anomaly_blocks", } // RequiredScopes returns the scopes used for login. diff --git a/internal/auth/auth_test.go b/internal/auth/auth_test.go index daef2ccfc..765bf706a 100644 --- a/internal/auth/auth_test.go +++ b/internal/auth/auth_test.go @@ -30,6 +30,7 @@ func TestRequiredScopes(t *testing.T) { "read:connections", "update:connections", "read:custom_domains", "read:client_keys", "read:logs", "read:tenant_settings", + "read:anomaly_blocks", "delete:anomaly_blocks", } for _, v := range list { diff --git a/internal/auth0/anomaly.go b/internal/auth0/anomaly.go new file mode 100644 index 000000000..82140ad0f --- /dev/null +++ b/internal/auth0/anomaly.go @@ -0,0 +1,17 @@ +package auth0 + +import "gopkg.in/auth0.v5/management" + +type AnomalyAPI interface { + // Check if a given IP address is blocked via the multiple user accounts + // trigger due to multiple failed logins. + // + // See: https://auth0.com/docs/api/management/v2#!/Anomaly/get_ips_by_id + CheckIP(ip string, opts ...management.RequestOption) (isBlocked bool, err error) + + // Unblock an IP address currently blocked by the multiple user accounts + // trigger due to multiple failed logins. + // + // See: https://auth0.com/docs/api/management/v2#!/Anomaly/delete_ips_by_id + UnblockIP(ip string, opts ...management.RequestOption) (err error) +} diff --git a/internal/auth0/auth0.go b/internal/auth0/auth0.go index cae1c53a9..d519d56dd 100644 --- a/internal/auth0/auth0.go +++ b/internal/auth0/auth0.go @@ -8,8 +8,10 @@ import ( // API mimics `management.Management`s general interface, except it refers to // the interfaces instead of the concrete structs. type API struct { + Anomaly AnomalyAPI Branding BrandingAPI Client ClientAPI + Connection ConnectionAPI CustomDomain CustomDomainAPI Log LogAPI ResourceServer ResourceServerAPI @@ -17,11 +19,11 @@ type API struct { Rule RuleAPI Tenant TenantAPI User UserAPI - Connection ConnectionAPI } func NewAPI(m *management.Management) *API { return &API{ + Anomaly: m.Anomaly, Branding: m.Branding, Client: m.Client, CustomDomain: m.CustomDomain, diff --git a/internal/cli/ips.go b/internal/cli/ips.go new file mode 100644 index 000000000..df562dda7 --- /dev/null +++ b/internal/cli/ips.go @@ -0,0 +1,109 @@ +package cli + +import ( + "fmt" + + "github.com/auth0/auth0-cli/internal/ansi" + "github.com/spf13/cobra" +) + +var ( + ipAddress = Argument{ + Name: "IP", + Help: "IP address to check.", + } +) + +func ipsCmd(cli *cli) *cobra.Command { + cmd := &cobra.Command{ + Use: "ips", + Short: "Manage blocked IP addresses", + Long: "Manage blocked IP addresses.", + } + + cmd.SetUsageTemplate(resourceUsageTemplate()) + cmd.AddCommand(checkIPCmd(cli)) + cmd.AddCommand(unblockIPCmd(cli)) + + return cmd +} + +func checkIPCmd(cli *cli) *cobra.Command { + var inputs struct { + IP string + } + + cmd := &cobra.Command{ + Use: "check", + Args: cobra.MaximumNArgs(1), + Short: "Check IP address", + Long: "Check whether a given IP address is blocked.", + Example: "auth0 ips check ", + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + if err := ipAddress.Ask(cmd, &inputs.IP); err != nil { + return err + } + } else { + inputs.IP = args[0] + } + + var isBlocked bool + + if err := ansi.Waiting(func() error { + var err error + isBlocked, err = cli.api.Anomaly.CheckIP(inputs.IP) + return err + }); err != nil { + return fmt.Errorf("An unexpected error occurred: %w", err) + } + + cli.renderer.Heading("IP") + + if isBlocked { + cli.renderer.Infof("The IP %s is blocked", inputs.IP) + } else { + cli.renderer.Infof("The IP %s is not blocked", inputs.IP) + } + + return nil + }, + } + + return cmd +} + +func unblockIPCmd(cli *cli) *cobra.Command { + var inputs struct { + IP string + } + + cmd := &cobra.Command{ + Use: "unblock", + Args: cobra.MaximumNArgs(1), + Short: "Unblock IP address", + Long: "Unblock an IP address which is currently blocked.", + Example: "auth0 ips unblock ", + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + if err := ipAddress.Ask(cmd, &inputs.IP); err != nil { + return err + } + } else { + inputs.IP = args[0] + } + + if err := ansi.Waiting(func() error { + return cli.api.Anomaly.UnblockIP(inputs.IP) + }); err != nil { + return fmt.Errorf("An unexpected error occurred: %w", err) + } + + cli.renderer.Heading("IP") + cli.renderer.Infof("The IP %s was unblocked", inputs.IP) + return nil + }, + } + + return cmd +} diff --git a/internal/cli/root.go b/internal/cli/root.go index 26a166ed0..11c171cb1 100644 --- a/internal/cli/root.go +++ b/internal/cli/root.go @@ -101,6 +101,7 @@ func Execute() { rootCmd.AddCommand(logoutCmd(cli)) rootCmd.AddCommand(brandingCmd(cli)) rootCmd.AddCommand(rolesCmd(cli)) + rootCmd.AddCommand(ipsCmd(cli)) // keep completion at the bottom: rootCmd.AddCommand(completionCmd(cli))