diff --git a/cmd/harbor/root/project/robot/create.go b/cmd/harbor/root/project/robot/create.go index a4df6f6fb..6b7cbabf2 100644 --- a/cmd/harbor/root/project/robot/create.go +++ b/cmd/harbor/root/project/robot/create.go @@ -148,7 +148,10 @@ Examples: } permissions = choices } else { - permissions = prompt.GetRobotPermissionsFromUser("project") + permissions, err = prompt.GetRobotPermissionsFromUser("project") + if err != nil { + return fmt.Errorf("failed to get project permissions: %v", err) + } if len(permissions) == 0 { msg := fmt.Errorf("no permissions selected, robot account needs at least one permission") return fmt.Errorf("failed to create robot: %v", utils.ParseHarborErrorMsg(msg)) diff --git a/cmd/harbor/root/project/robot/delete.go b/cmd/harbor/root/project/robot/delete.go index 157a55d95..1473d7b3d 100644 --- a/cmd/harbor/root/project/robot/delete.go +++ b/cmd/harbor/root/project/robot/delete.go @@ -88,13 +88,19 @@ Examples: if err != nil { return fmt.Errorf("failed to get project by name %s: %v", ProjectName, utils.ParseHarborErrorMsg(err)) } - robotID = prompt.GetRobotIDFromUser(int64(project.Payload.ProjectID)) + robotID, err = prompt.GetRobotIDFromUser(int64(project.Payload.ProjectID)) + if err != nil { + return fmt.Errorf("failed to get robot ID from user: %v", utils.ParseHarborErrorMsg(err)) + } } else { projectID, err := prompt.GetProjectIDFromUser() if err != nil { log.Fatalf("failed to get project by id %d: %v", projectID, utils.ParseHarborErrorMsg(err)) } - robotID = prompt.GetRobotIDFromUser(projectID) + robotID, err = prompt.GetRobotIDFromUser(projectID) + if err != nil { + return fmt.Errorf("failed to get robot ID from user: %v", utils.ParseHarborErrorMsg(err)) + } } err = api.DeleteRobot(robotID) if err != nil { diff --git a/cmd/harbor/root/project/robot/refresh.go b/cmd/harbor/root/project/robot/refresh.go index 1d7b1c2e5..ac8c1fa36 100644 --- a/cmd/harbor/root/project/robot/refresh.go +++ b/cmd/harbor/root/project/robot/refresh.go @@ -85,7 +85,10 @@ Examples: if err != nil { log.Fatalf("failed to get project by id %d: %v", projectID, utils.ParseHarborErrorMsg(err)) } - robotID = prompt.GetRobotIDFromUser(projectID) + robotID, err = prompt.GetRobotIDFromUser(projectID) + if err != nil { + log.Fatalf("failed to get robot ID from user: %v", utils.ParseHarborErrorMsg(err)) + } } if secret != "" { diff --git a/cmd/harbor/root/project/robot/update.go b/cmd/harbor/root/project/robot/update.go index c4cf8a042..7dbaefd35 100644 --- a/cmd/harbor/root/project/robot/update.go +++ b/cmd/harbor/root/project/robot/update.go @@ -92,13 +92,19 @@ Examples: if err != nil { log.Fatalf("failed to get project by name %s: %v", ProjectName, err) } - robotID = prompt.GetRobotIDFromUser(int64(project.Payload.ProjectID)) + robotID, err = prompt.GetRobotIDFromUser(int64(project.Payload.ProjectID)) + if err != nil { + log.Fatalf("failed to get robot ID from user: %v", utils.ParseHarborErrorMsg(err)) + } } else { projectID, err := prompt.GetProjectIDFromUser() if err != nil { log.Fatalf("failed to get project by id %d: %v", projectID, utils.ParseHarborErrorMsg(err)) } - robotID = prompt.GetRobotIDFromUser(projectID) + robotID, err = prompt.GetRobotIDFromUser(projectID) + if err != nil { + log.Fatalf("failed to get robot ID from user: %v", utils.ParseHarborErrorMsg(err)) + } } robot, err := api.GetRobot(robotID) @@ -138,7 +144,10 @@ Examples: } permissions = choices } else { - permissions = prompt.GetRobotPermissionsFromUser("project") + permissions, err = prompt.GetRobotPermissionsFromUser("project") + if err != nil { + log.Fatalf("failed to get project permissions: %v", utils.ParseHarborErrorMsg(err)) + } } // []Permission to []*Access diff --git a/cmd/harbor/root/project/robot/view.go b/cmd/harbor/root/project/robot/view.go index b9554be3f..874006fce 100644 --- a/cmd/harbor/root/project/robot/view.go +++ b/cmd/harbor/root/project/robot/view.go @@ -75,13 +75,19 @@ Examples: if err != nil { log.Fatalf("failed to get project by name %s: %v", ProjectName, err) } - robotID = prompt.GetRobotIDFromUser(int64(project.Payload.ProjectID)) + robotID, err = prompt.GetRobotIDFromUser(int64(project.Payload.ProjectID)) + if err != nil { + log.Fatalf("failed to get robot ID from user: %v", utils.ParseHarborErrorMsg(err)) + } } else { projectID, err := prompt.GetProjectIDFromUser() if err != nil { log.Fatalf("failed to get project by id %d: %v", projectID, utils.ParseHarborErrorMsg(err)) } - robotID = prompt.GetRobotIDFromUser(projectID) + robotID, err = prompt.GetRobotIDFromUser(projectID) + if err != nil { + log.Fatalf("failed to get robot ID from user: %v", utils.ParseHarborErrorMsg(err)) + } } robot, err = api.GetRobot(robotID) diff --git a/cmd/harbor/root/robot/create.go b/cmd/harbor/root/robot/create.go index 268f276db..ee73feafe 100644 --- a/cmd/harbor/root/robot/create.go +++ b/cmd/harbor/root/robot/create.go @@ -189,12 +189,19 @@ func handleInteractiveInput(opts *create.CreateView, all bool, permissions *[]mo func getSystemPermissions(all bool, permissions *[]models.Permission) error { if len(*permissions) == 0 { if all { - perms, _ := api.GetPermissions() + perms, err := api.GetPermissions() + if err != nil { + return fmt.Errorf("failed to get system permissions: %v", utils.ParseHarborErrorMsg(err)) + } for _, perm := range perms.Payload.System { *permissions = append(*permissions, *perm) } } else { - *permissions = prompt.GetRobotPermissionsFromUser("system") + var err error + *permissions, err = prompt.GetRobotPermissionsFromUser("system") + if err != nil { + return fmt.Errorf("failed to create robot: %v", utils.ParseHarborErrorMsg(err)) + } if len(*permissions) == 0 { return fmt.Errorf("failed to create robot: %v", utils.ParseHarborErrorMsg(fmt.Errorf("no permissions selected, robot account needs at least one permission"))) @@ -231,7 +238,10 @@ func handleMultipleProjectsPermissions(projectPermissionsMap map[string][]models if len(selectedProjects) > 0 { fmt.Println("Select permissions to apply to all selected projects:") - projectPermissions := prompt.GetRobotPermissionsFromUser("project") + projectPermissions, err := prompt.GetRobotPermissionsFromUser("project") + if err != nil { + return fmt.Errorf("failed to get project permissions: %v", err) + } for _, projectName := range selectedProjects { projectPermissionsMap[projectName] = projectPermissions } @@ -251,7 +261,10 @@ func handlePerProjectPermissions(opts *create.CreateView, projectPermissionsMap return fmt.Errorf("project name cannot be empty") } - projectPermissionsMap[projectName] = prompt.GetRobotPermissionsFromUser("project") + projectPermissionsMap[projectName], err = prompt.GetRobotPermissionsFromUser("project") + if err != nil { + return fmt.Errorf("failed to get project permissions: %v", err) + } moreProjects, err := promptMoreProjects() if err != nil { @@ -262,7 +275,10 @@ func handlePerProjectPermissions(opts *create.CreateView, projectPermissionsMap } } } else { - projectPermissions := prompt.GetRobotPermissionsFromUser("project") + projectPermissions, err := prompt.GetRobotPermissionsFromUser("project") + if err != nil { + return fmt.Errorf("failed to get project permissions: %v", err) + } projectPermissionsMap[opts.ProjectName] = projectPermissions } @@ -301,7 +317,12 @@ func buildMergedPermissions(projectPermissionsMap map[string][]models.Permission func createRobotAndHandleResponse(opts *create.CreateView, exportToFile bool) error { response, err := api.CreateRobot(*opts) if err != nil { - return fmt.Errorf("failed to create robot: %v", utils.ParseHarborErrorMsg(err)) + errorCode := utils.ParseHarborErrorCode(err) + if errorCode == "403" { + return fmt.Errorf("Permission denied: (Project) Admin privileges are required to execute this command.") + } else { + return fmt.Errorf("failed to create robot: %v", utils.ParseHarborErrorMsg(err)) + } } logrus.Infof("Successfully created robot account '%s' (ID: %d)", diff --git a/cmd/harbor/root/robot/delete.go b/cmd/harbor/root/robot/delete.go index 70122427a..7cf95e6af 100644 --- a/cmd/harbor/root/robot/delete.go +++ b/cmd/harbor/root/robot/delete.go @@ -60,15 +60,28 @@ Examples: robotName := args[0] robot, err := api.GetRobotByName(robotName) if err != nil { - return fmt.Errorf("failed to delete robots: %v", utils.ParseHarborErrorMsg(err)) + errorCode := utils.ParseHarborErrorCode(err) + if errorCode == "403" { + return fmt.Errorf("Permission denied: (Project) Admin privileges are required to execute this command.") + } else { + return fmt.Errorf("failed to get robot: %v", utils.ParseHarborErrorMsg(err)) + } } robotID = robot.ID } else { - robotID = prompt.GetRobotIDFromUser(-1) + robotID, err = prompt.GetRobotIDFromUser(-1) + if err != nil { + return fmt.Errorf("failed to get robot ID from user: %v", utils.ParseHarborErrorMsg(err)) + } } err = api.DeleteRobot(robotID) if err != nil { - return fmt.Errorf("failed to delete robots: %v", utils.ParseHarborErrorMsg(err)) + errorCode := utils.ParseHarborErrorCode(err) + if errorCode == "403" { + return fmt.Errorf("Permission denied: (Project) Admin privileges are required to execute this command.") + } else { + return fmt.Errorf("failed to delete robot: %v", utils.ParseHarborErrorMsg(err)) + } } fmt.Printf("Robot account (ID: %d) was successfully deleted\n", robotID) return nil diff --git a/cmd/harbor/root/robot/list.go b/cmd/harbor/root/robot/list.go index 80b4427d9..682ab9519 100644 --- a/cmd/harbor/root/robot/list.go +++ b/cmd/harbor/root/robot/list.go @@ -14,6 +14,8 @@ package robot import ( + "fmt" + "github.com/goharbor/harbor-cli/pkg/api" "github.com/goharbor/harbor-cli/pkg/utils" "github.com/goharbor/harbor-cli/pkg/views/robot/list" @@ -61,10 +63,15 @@ Examples: # Get robot details in JSON format harbor-cli robot list --output-format json`, Args: cobra.MaximumNArgs(0), - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { robots, err := api.ListRobot(opts) if err != nil { - log.Errorf("failed to get robots list: %v", utils.ParseHarborErrorMsg(err)) + errorCode := utils.ParseHarborErrorCode(err) + if errorCode == "403" { + return fmt.Errorf("Permission denied: (Project) Admin privileges are required to execute this command.") + } else { + return fmt.Errorf("failed to list robots: %v", utils.ParseHarborErrorMsg(err)) + } } formatFlag := viper.GetString("output-format") @@ -76,6 +83,7 @@ Examples: } else { list.ListRobots(robots.Payload) } + return nil }, } diff --git a/cmd/harbor/root/robot/refresh.go b/cmd/harbor/root/robot/refresh.go index 20d8bc6d9..759c67e55 100644 --- a/cmd/harbor/root/robot/refresh.go +++ b/cmd/harbor/root/robot/refresh.go @@ -81,7 +81,10 @@ Examples: log.Fatalf("failed to parse robot ID: %v", err) } } else { - robotID = prompt.GetRobotIDFromUser(-1) + robotID, err = prompt.GetRobotIDFromUser(-1) + if err != nil { + log.Fatalf("failed to get robot ID from user: %v", utils.ParseHarborErrorMsg(err)) + } } if secret != "" { @@ -96,7 +99,12 @@ Examples: response, err := api.RefreshSecret(secret, robotID) if err != nil { - log.Fatalf("failed to refresh robot secret: %v\n", err) + errorCode := utils.ParseHarborErrorCode(err) + if errorCode == "403" { + log.Fatalf("Permission denied: (Project) Admin privileges are required to execute this command.\n") + } else { + log.Fatalf("failed to refresh robot secret: %v\n", utils.ParseHarborErrorMsg(err)) + } } log.Info("Secret updated successfully.") diff --git a/cmd/harbor/root/robot/update.go b/cmd/harbor/root/robot/update.go index 7da9bcf08..981625f01 100644 --- a/cmd/harbor/root/robot/update.go +++ b/cmd/harbor/root/robot/update.go @@ -95,7 +95,10 @@ Examples: return fmt.Errorf("failed to parse robot ID: %v", err) } } else { - robotID = prompt.GetRobotIDFromUser(-1) + robotID, err = prompt.GetRobotIDFromUser(-1) + if err != nil { + return fmt.Errorf("failed to get robot ID from user: %v", utils.ParseHarborErrorMsg(err)) + } } // Get current robot configuration @@ -306,7 +309,10 @@ func getSystemPermissionsForUpdate(all bool, permissions *[]models.Permission) e *permissions = append(*permissions, *perm) } } else { - newPermissions := prompt.GetRobotPermissionsFromUser("system") + newPermissions, err := prompt.GetRobotPermissionsFromUser("system") + if err != nil { + return fmt.Errorf("failed to update robot: %v", utils.ParseHarborErrorMsg(err)) + } if len(newPermissions) == 0 { return fmt.Errorf("failed to update robot: %v", utils.ParseHarborErrorMsg(fmt.Errorf("no permissions selected, robot account needs at least one permission"))) @@ -377,7 +383,10 @@ func handleMultipleProjectsPermissionsForUpdate(projectPermissionsMap map[string if len(selectedProjects) > 0 { fmt.Println("Select permissions to apply to all selected projects:") - projectPermissions := prompt.GetRobotPermissionsFromUser("project") + projectPermissions, err := prompt.GetRobotPermissionsFromUser("project") + if err != nil { + return fmt.Errorf("failed to update robot: %v", utils.ParseHarborErrorMsg(err)) + } // Validate project permissions validProjectPerms, err := validateProjectPermissions(projectPermissions) @@ -449,7 +458,10 @@ func handlePerProjectPermissionsForUpdate(projectPermissionsMap map[string][]mod // Update permissions for selected projects for _, project := range selectedProjects { fmt.Printf("Updating permissions for project: %s\n", project) - projectPerms := prompt.GetRobotPermissionsFromUser("project") + projectPerms, err := prompt.GetRobotPermissionsFromUser("project") + if err != nil { + return fmt.Errorf("failed to update robot: %v", utils.ParseHarborErrorMsg(err)) + } // Validate project permissions validProjectPerms, err := validateProjectPermissions(projectPerms) @@ -474,7 +486,10 @@ func handlePerProjectPermissionsForUpdate(projectPermissionsMap map[string][]mod return fmt.Errorf("project name cannot be empty") } - projectPerms := prompt.GetRobotPermissionsFromUser("project") + projectPerms, err := prompt.GetRobotPermissionsFromUser("project") + if err != nil { + return fmt.Errorf("failed to update robot: %v", utils.ParseHarborErrorMsg(err)) + } // Validate project permissions validProjectPerms, err := validateProjectPermissions(projectPerms) diff --git a/cmd/harbor/root/robot/view.go b/cmd/harbor/root/robot/view.go index c160034b0..b9fdf357c 100644 --- a/cmd/harbor/root/robot/view.go +++ b/cmd/harbor/root/robot/view.go @@ -14,13 +14,14 @@ package robot import ( + "fmt" "strconv" "github.com/goharbor/go-client/pkg/sdk/v2.0/client/robot" "github.com/goharbor/harbor-cli/pkg/api" "github.com/goharbor/harbor-cli/pkg/prompt" + "github.com/goharbor/harbor-cli/pkg/utils" "github.com/goharbor/harbor-cli/pkg/views/robot/view" - log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -56,7 +57,7 @@ Examples: # Interactive selection (will prompt for robot) harbor-cli robot view`, Args: cobra.MaximumNArgs(1), - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { var ( robot *robot.GetRobotByIDOK robotID int64 @@ -66,20 +67,29 @@ Examples: if len(args) == 1 { robotID, err = strconv.ParseInt(args[0], 10, 64) if err != nil { - log.Fatalf("failed to parse robot ID: %v", err) + return fmt.Errorf("failed to parse robot ID %q: %v", args[0], err) } } else { - robotID = prompt.GetRobotIDFromUser(-1) + robotID, err = prompt.GetRobotIDFromUser(-1) + if err != nil { + return fmt.Errorf("failed to get robot ID from user: %v", utils.ParseHarborErrorMsg(err)) + } } robot, err = api.GetRobot(robotID) if err != nil { - log.Fatalf("failed to get robot: %v", err) + errorCode := utils.ParseHarborErrorCode(err) + if errorCode == "403" { + return fmt.Errorf("Permission denied: (Project) Admin privileges are required to execute this command.") + } else { + return fmt.Errorf("failed to get robot: %v", utils.ParseHarborErrorMsg(err)) + } } // Convert to a list and display // robots := &models.Robot{robot.Payload} view.ViewRobot(robot.Payload) + return nil }, } diff --git a/pkg/prompt/prompt.go b/pkg/prompt/prompt.go index e33e4ac2b..3b0c55379 100644 --- a/pkg/prompt/prompt.go +++ b/pkg/prompt/prompt.go @@ -316,16 +316,28 @@ func GetActiveContextFromUser() (string, error) { return res, nil } -func GetRobotPermissionsFromUser(kind string) []models.Permission { +func GetRobotPermissionsFromUser(kind string) ([]models.Permission, error) { + type result struct { + permissions []models.Permission + err error + } permissions := make(chan []models.Permission) + results := make(chan result) go func() { - response, _ := api.GetPermissions() + response, err := api.GetPermissions() + if err != nil { + fmt.Println("Permission denied: (Project) Admin privileges are required to execute this command.") + results <- result{nil, err} + return + } robotView.ListPermissions(response.Payload, kind, permissions) + results <- result{<-permissions, nil} }() - return <-permissions + res := <-results + return res.permissions, res.err } -func GetRobotIDFromUser(projectID int64) int64 { +func GetRobotIDFromUser(projectID int64) (int64, error) { robotID := make(chan int64) var opts api.ListFlags if projectID != -1 { @@ -333,10 +345,25 @@ func GetRobotIDFromUser(projectID int64) int64 { } go func() { - response, _ := api.ListRobot(opts) + response, err := api.ListRobot(opts) + if err != nil { + errorCode := utils.ParseHarborErrorCode(err) + if errorCode == "403" { + fmt.Println("Permission denied: (Project) Admin privileges are required to execute this command.") + } else { + fmt.Printf("failed to list robots: %v\n", utils.ParseHarborErrorMsg(err)) + } + close(robotID) + return + } robotView.ListRobot(response.Payload, robotID) }() - return <-robotID + + id, ok := <-robotID + if !ok { + return 0, errors.New("failed to retrieve robot ID") + } + return id, nil } func GetReplicationPolicyFromUser() int64 { diff --git a/pkg/utils/error.go b/pkg/utils/error.go index c1d05824b..bc885116a 100644 --- a/pkg/utils/error.go +++ b/pkg/utils/error.go @@ -53,7 +53,28 @@ func ParseHarborErrorMsg(err error) string { } func ParseHarborErrorCode(err error) string { - parts := strings.Split(err.Error(), "]") + if err == nil { + return "" + } + + errMsg := err.Error() + + // Handle: "response status code does not match any response statuses defined for this endpoint in the swagger spec (status 403): {}" + if strings.Contains(errMsg, "(status ") { + // Extract content between "(status " and ")" + startIdx := strings.Index(errMsg, "(status ") + if startIdx != -1 { + startIdx += len("(status ") + endIdx := strings.Index(errMsg[startIdx:], ")") + if endIdx != -1 { + statusCode := strings.TrimSpace(errMsg[startIdx : startIdx+endIdx]) + return statusCode + } + } + } + + // Handle: "[code] message" format + parts := strings.Split(errMsg, "]") if len(parts) >= 2 { codePart := strings.TrimSpace(parts[1]) if strings.HasPrefix(codePart, "[") && len(codePart) == 4 { @@ -61,5 +82,6 @@ func ParseHarborErrorCode(err error) string { return code } } + return "" }