Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI-80: user-blocks commands with user_id #201

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion internal/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ var requiredScopes = []string{
"create:clients", "delete:clients", "read:clients", "update:clients",
"create:resource_servers", "delete:resource_servers", "read:resource_servers", "update:resource_servers",
"create:rules", "delete:rules", "read:rules", "update:rules",
"read:client_keys", "read:logs",
"read:client_keys", "read:logs", "read:users", "update:users",
}

// SecretStore provides secure storage for sensitive data
Expand Down
2 changes: 2 additions & 0 deletions internal/auth0/auth0.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type API struct {
Log LogAPI
ResourceServer ResourceServerAPI
Rule RuleAPI
User UserAPI
}

func NewAPI(m *management.Management) *API {
Expand All @@ -28,6 +29,7 @@ func NewAPI(m *management.Management) *API {
Log: m.Log,
ResourceServer: m.ResourceServer,
Rule: m.Rule,
User: m.User,
}
}

Expand Down
14 changes: 14 additions & 0 deletions internal/auth0/user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//go:generate mockgen -source=user.go -destination=user_mock.go -package=auth0

package auth0

import "gopkg.in/auth0.v5/management"

type UserAPI interface {
// Retrieves a list of blocked IP addresses of a particular user.
Blocks(id string, opts ...management.RequestOption) ([]*management.UserBlock, error)

// Unblock a user that was blocked due to an excessive amount of incorrectly
// provided credentials.
Unblock(id string, opts ...management.RequestOption) error
}
1 change: 1 addition & 0 deletions internal/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ func Execute() {
rootCmd.AddCommand(rulesCmd(cli))
rootCmd.AddCommand(quickstartsCmd(cli))
rootCmd.AddCommand(apisCmd(cli))
rootCmd.AddCommand(userBlocksCmd(cli))
rootCmd.AddCommand(testCmd(cli))
rootCmd.AddCommand(logsCmd(cli))
rootCmd.AddCommand(logoutCmd(cli))
Expand Down
110 changes: 110 additions & 0 deletions internal/cli/user_blocks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package cli

import (
"errors"
"fmt"

"github.com/auth0/auth0-cli/internal/ansi"
"github.com/spf13/cobra"
"gopkg.in/auth0.v5/management"
)

var (
userID = Argument{
Name: "user_id",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Name: "user_id",
Name: "User ID",

Help: "user_id of the user.",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Help: "user_id of the user.",
Help: "Id of the user.",

}
)

func userBlocksCmd(cli *cli) *cobra.Command {
cmd := &cobra.Command{
Use: "user-blocks",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please put it under users

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a subcommand: auth0 users blocks

Short: "Manage brute-force protection user blocks.",
}

cmd.SetUsageTemplate(resourceUsageTemplate())
cmd.AddCommand(listUserBlocksByUserIdCmd(cli))
cmd.AddCommand(deleteUserBlocksByUserIdCmd(cli))
return cmd
}

func listUserBlocksByUserIdCmd(cli *cli) *cobra.Command {
var inputs struct {
user_id string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do not use snake_case

}

cmd := &cobra.Command{
Use: "listByUserId",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use list. Entire command should be auth0 users blocks list.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can list by user_id or list by email / phonenumber / user_identifier. It's a different API call, what would a good way to name the handle the two different input types?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The command should accept any of these as a single argument, and then disambiguate. It's the easiest for the user, that remains unaware (as they should) of those implementation details.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e.g. auth0 apis show accepts an API id or the audience.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the end user, there's no difference.

Args: cobra.MaximumNArgs(1),
Short: "List user-blocks by user_id",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do not include the API2 entities as-is, instead describe what the command does in the context of a single user.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assume the user knows nothing about API2.

Long: `List user-blocks by user_id:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.


auth0 user-blocks listByUserId <user_id>
`,
PreRun: func(cmd *cobra.Command, args []string) {
prepareInteractivity(cmd)
},
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
inputs.user_id = args[0]
} else {
return errors.New("user_id is required.")
}

var userBlocks []*management.UserBlock

err := ansi.Waiting(func() error {
var err error
userBlocks, err = cli.api.User.Blocks(inputs.user_id)
return err
})

if err != nil {
return fmt.Errorf("Unable to load user blocks %v, error: %w", inputs.user_id, err)
}

cli.renderer.UserBlocksList(userBlocks)
return nil
},
}

return cmd
}

func deleteUserBlocksByUserIdCmd(cli *cli) *cobra.Command {
var inputs struct {
user_id string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do not use snake_case.

}

cmd := &cobra.Command{
Use: "deleteByUserId",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please usedelete. Entire command should be auth0 users blocks delete.

Args: cobra.MaximumNArgs(1),
Short: "Delete all user-blocks by user_id",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do not include the API2 entities as-is, instead describe what the command does in the context of a single user.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assume the user knows nothing about API2.

Long: `Delete all user-blocks by user_id:
Copy link
Contributor

@Widcket Widcket Mar 27, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.


auth0 user-blocks deleteByUserId <user_id>
`,
PreRun: func(cmd *cobra.Command, args []string) {
prepareInteractivity(cmd)
},
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
inputs.user_id = args[0]
} else {
return errors.New("user_id is required.")
}

err := ansi.Spinner("Deleting blocks for user...", func() error {
return cli.api.User.Unblock(inputs.user_id)
})

if err != nil {
return err
}

return nil
},
}

return cmd
}
56 changes: 56 additions & 0 deletions internal/display/user_blocks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package display

import (
"gopkg.in/auth0.v5/management"
)

type userBlockView struct {
Identifier string
IP string
}

func (v *userBlockView) AsTableHeader() []string {
return []string{"Identifier", "IP"}
}

func (v *userBlockView) AsTableRow() []string {
return []string{v.Identifier, v.IP}
}

func (v *userBlockView) KeyValues() [][]string {
return [][]string{
[]string{"Identifier", v.Identifier},
[]string{"IP", v.IP},
}
}

func (r *Renderer) UserBlocksList(userBlocks []*management.UserBlock) {
resource := "userBlocks"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
resource := "userBlocks"
resource := "user blocks"


r.Heading(resource)

if len(userBlocks) == 0 {
r.EmptyState(resource)
r.Infof("No blocks for user.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

r.EmptyState(resource) and r.Infof("No blocks for user.") do the same thing, please remove the last one.

return
}

var res []View

for _, userBlock := range userBlocks {
res = append(res, &userBlockView{
Identifier: *userBlock.Identifier,
IP: *userBlock.IP,
})
}

r.Results(res)

}

func makeUserBlockView(userBlock *management.UserBlock) *userBlockView {
return &userBlockView{
Identifier: userBlock.GetIdentifier(),
IP: userBlock.GetIP(),
}
}