goharbor / harbor-cli

[Sandbox] Official Harbor CLI
Apache License 2.0
38 stars 45 forks source link

Autogenerate the Name of Credential in Login Flow #148

Closed nox1134 closed 1 month ago

nox1134 commented 3 months ago

Fixes #145

Added a function generateCredentialName in login.go to automatically create a credential name using the server URL and username. The credential name is generated by combining the server address (stripped of protocol and port) and the username.

nox1134 commented 3 months ago

Hi! Should I also update the login_test to verify that the credential name is generated correctly?

Althaf66 commented 1 month ago

@nox1134 Make changes in pkg/views/login/create.go

package login

import (
    "errors"

    "github.com/charmbracelet/huh"
    log "github.com/sirupsen/logrus"
)

type LoginView struct {
    Server   string
    Username string
    Password string
    Name     string
}

func CreateView(loginView *LoginView) {
    theme := huh.ThemeCharm()
    err := huh.NewForm(
        huh.NewGroup(
            huh.NewInput().
                Title("Server").
                Value(&loginView.Server).
                Validate(func(str string) error {
                    if str == "" {
                        return errors.New("server cannot be empty")
                    }
                    return nil
                }),
            huh.NewInput().
                Title("User Name").
                Value(&loginView.Username).
                Validate(func(str string) error {
                    if str == "" {
                        return errors.New("username cannot be empty")
                    }
                    return nil
                }),
            huh.NewInput().
                Title("Password").
                EchoMode(huh.EchoModePassword).
                Value(&loginView.Password).
                Validate(func(str string) error {
                    if str == "" {
                        return errors.New("password cannot be empty")
                    }
                    return nil
                }),
            huh.NewInput().
                Title("Name of Credential").
                Value(&loginView.Name),
        ),
    ).WithTheme(theme).Run()

    if err != nil {
        log.Fatal(err)
    }

}
Althaf66 commented 1 month ago

Changes in cmd/harbor/root/login.go

package root

import (
    "context"
    "fmt"
    "strings"

    "github.com/goharbor/go-client/pkg/harbor"
    "github.com/goharbor/go-client/pkg/sdk/v2.0/client/user"
    "github.com/goharbor/harbor-cli/pkg/utils"
    "github.com/goharbor/harbor-cli/pkg/views/login"
    "github.com/spf13/cobra"
)

var (
    serverAddress string
    Username      string
    Password      string
    Name          string
)

// LoginCommand creates a new `harbor login` command
func LoginCommand() *cobra.Command {
    cmd := &cobra.Command{
        Use:   "login [server]",
        Short: "Log in to Harbor registry",
        Long:  "Authenticate with Harbor Registry.",
        Args:  cobra.MaximumNArgs(1),
        RunE: func(cmd *cobra.Command, args []string) error {
            if len(args) > 0 {
                serverAddress = args[0]
            }

            if Name == "" {
                // Auto-generate the credential name if not provided
                Name = generateCredentialName(serverAddress, Username)
            }

            loginView := login.LoginView{
                Server:   serverAddress,
                Username: Username,
                Password: Password,
                Name:     Name,
            }

            var err error

            if loginView.Server != "" && loginView.Username != "" && loginView.Password != "" &&
                loginView.Name != "" {
                err = runLogin(loginView)
            } else {
                err = createLoginView(&loginView)
            }

            if err != nil {
                return err
            }
            return nil
        },
    }

    flags := cmd.Flags()
    flags.StringVarP(&Name, "name", "", "", "name for the set of credentials")
    flags.StringVarP(&Username, "username", "u", "", "Username")
    flags.StringVarP(&Password, "password", "p", "", "Password")

    return cmd
}

// generateCredentialName creates a default credential name based on server and username
func generateCredentialName(server, username string) string {
    if strings.HasPrefix(server, "http://") {
        server = strings.ReplaceAll(server, "http://", "")
    }
    if strings.HasPrefix(server, "https://") {
        server = strings.ReplaceAll(server, "https://", "")
    }
    if username != "" {
        return fmt.Sprintf("%s@%s", username, server)
    }
    return server
}

func createLoginView(loginView *login.LoginView) error {
    if loginView == nil {
        loginView = &login.LoginView{
            Server:   "",
            Username: "",
            Password: "",
            Name:     "",
        }
    }
    login.CreateView(loginView)
    return runLogin(*loginView)
}

func runLogin(opts login.LoginView) error {
    opts.Server = utils.FormatUrl(opts.Server)

    clientConfig := &harbor.ClientSetConfig{
        URL:      opts.Server,
        Username: opts.Username,
        Password: opts.Password,
    }
    client := utils.GetClientByConfig(clientConfig)

    ctx := context.Background()
    _, err := client.User.GetCurrentUserInfo(ctx, &user.GetCurrentUserInfoParams{})
    if err != nil {
        return fmt.Errorf("login failed, please check your credentials: %s", err)
    }
    if opts.Name == "" {
        opts.Name = generateCredentialName(opts.Server,opts.Username)
    }

    cred := utils.Credential{
        Name:          opts.Name,
        Username:      opts.Username,
        Password:      opts.Password,
        ServerAddress: opts.Server,
    }

    if err = utils.AddCredentialsToConfigFile(cred, utils.DefaultConfigPath); err != nil {
        return fmt.Errorf("failed to store the credential: %s", err)
    }
    return nil
}
nox1134 commented 1 month ago

Hi @Althaf66! Thank you! I have made the requested changes.

Althaf66 commented 1 month ago

ready for review