From 12098e0f6ed409bd130de6a874feb9e50dcfdce2 Mon Sep 17 00:00:00 2001 From: Christian Dupuis Date: Mon, 19 Sep 2022 15:16:09 +0200 Subject: [PATCH] Remove --api-key parameter fixes #1 Signed-off-by: Christian Dupuis --- README.md | 16 ++++++--- main.go | 100 +++++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 91 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 81e776d..ab6d17b 100644 --- a/README.md +++ b/README.md @@ -38,23 +38,29 @@ Alternatively, you can install manually by following these steps: To detect base images for local or remote images, use the following command: ```shell -$ docker base detect +$ docker base detect ``` -`` can either be a local image id or fully qualified image name from a remote registry. +`` can either be a local image id or fully qualified image name from a remote registry. ### `docker base login` To authenticate with the Atomist data plane, use the following command: ```shell -$ docker base login --workspace --api-key +$ docker base login ``` +For the security reasons the command does not accept an API key as command parameter. Instead, an API key can be passed +in via stdin with the parameter `--api-key-stdin`. + +The `login` command will also check the legacy `ATOMIST_API_KEY` environment variable. + Authentication is not required. If not authenticated, the plugin will only use public data from Docker Official Images, -Docker Verified Publishers or Docker-sponsored Open Source. +Docker Verified Publishers or Docker-sponsored Open Source. Without authentication the `detect` command will not take +into account your own data on Docker Hub when searching for matching base images. -Visit [dso.docker.com](https://dso.docker.com/r/auth/integrations) to obtain a `workspace id` and `api key`. +Visit [dso.docker.com](https://dso.docker.com/r/auth/integrations) to obtain a `WORKSPACE ID` and `API KEY`. ### `docker base logout` diff --git a/main.go b/main.go index 83879c2..c8e8fb6 100644 --- a/main.go +++ b/main.go @@ -17,7 +17,11 @@ package main import ( + "bufio" "fmt" + "io" + "os" + "strings" "github.com/docker/base-cli-plugin/commands" "github.com/docker/base-cli-plugin/internal" @@ -25,6 +29,7 @@ import ( "github.com/docker/cli/cli-plugins/manager" "github.com/docker/cli/cli-plugins/plugin" "github.com/docker/cli/cli/command" + "github.com/moby/term" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -32,7 +37,7 @@ import ( func main() { plugin.Run(func(dockerCli command.Cli) *cobra.Command { var ( - workspace, apiKey string + apiKeyStdin bool ) logout := &cobra.Command{ @@ -46,23 +51,29 @@ func main() { } login := &cobra.Command{ - Use: "login", - Short: "Authenticate with an Atomist workspace", - RunE: func(cmd *cobra.Command, _ []string) error { - if !query.CheckAuth(workspace, apiKey) { - return errors.New("login failed") - } else { + Use: "login WORKSPACE", + Short: "Authenticate with Atomist workspace", + RunE: func(cmd *cobra.Command, args []string) error { + workspace, err := readWorkspace(args, dockerCli) + if err != nil { + return err + } + apiKey, err := readApiKey(apiKeyStdin, dockerCli) + if err != nil { + return err + } + if query.CheckAuth(workspace, apiKey) { + fmt.Println("Login successful") dockerCli.ConfigFile().SetPluginConfig("base", "workspace", workspace) dockerCli.ConfigFile().SetPluginConfig("base", "api-key", apiKey) return dockerCli.ConfigFile().Save() + } else { + return errors.New("Login failed") } }, } loginFlags := login.Flags() - loginFlags.StringVar(&workspace, "workspace", "", "Atomist workspace") - loginFlags.StringVar(&apiKey, "api-key", "", "Atomist API key") - login.MarkFlagRequired("workspace") - login.MarkFlagRequired("api-key") + loginFlags.BoolVar(&apiKeyStdin, "api-key-stdin", false, "Atomist API key") base := &cobra.Command{ Use: "detect [OPTIONS] IMAGE", @@ -74,19 +85,12 @@ func main() { } return fmt.Errorf(`"docker base detect" requires exactly 1 argument`) } - if workspace == "" { - workspace, _ = dockerCli.ConfigFile().PluginConfig("base", "workspace") - } - if apiKey == "" { - apiKey, _ = dockerCli.ConfigFile().PluginConfig("base", "api-key") - } + workspace, _ := dockerCli.ConfigFile().PluginConfig("base", "workspace") + apiKey, _ := dockerCli.ConfigFile().PluginConfig("base", "api-key") return commands.Detect(dockerCli, args[0], workspace, apiKey) }, } - baseFlags := base.Flags() - baseFlags.StringVar(&workspace, "workspace", "", "Atomist workspace") - baseFlags.StringVar(&apiKey, "api-key", "", "Atomist API key") base.MarkFlagRequired("image") cmd := &cobra.Command{ @@ -103,3 +107,59 @@ func main() { Version: internal.FromBuild().Version, }) } + +func readWorkspace(args []string, cli command.Cli) (string, error) { + var workspace string + if len(args) == 1 { + workspace = args[0] + } else { + fmt.Fprintf(cli.Out(), "Workspace: ") + + workspace = readInput(cli.In(), cli.Out()) + if workspace == "" { + return "", errors.Errorf("Error: Workspace required") + } + } + return workspace, nil +} + +func readApiKey(apiKeyStdin bool, cli command.Cli) (string, error) { + var apiKey string + + if apiKeyStdin { + contents, err := io.ReadAll(cli.In()) + if err != nil { + return "", err + } + + apiKey = strings.TrimSuffix(string(contents), "\n") + apiKey = strings.TrimSuffix(apiKey, "\r") + } else if v, ok := os.LookupEnv("ATOMIST_API_KEY"); v != "" && ok { + apiKey = v + } else { + oldState, err := term.SaveState(cli.In().FD()) + if err != nil { + return "", err + } + fmt.Fprintf(cli.Out(), "API key: ") + term.DisableEcho(cli.In().FD(), oldState) + + apiKey = readInput(cli.In(), cli.Out()) + fmt.Fprint(cli.Out(), "\n") + term.RestoreTerminal(cli.In().FD(), oldState) + if apiKey == "" { + return "", errors.Errorf("Error: API key required") + } + } + return apiKey, nil +} + +func readInput(in io.Reader, out io.Writer) string { + reader := bufio.NewReader(in) + line, _, err := reader.ReadLine() + if err != nil { + fmt.Fprintln(out, err.Error()) + os.Exit(1) + } + return string(line) +}