summaryrefslogtreecommitdiffstats
path: root/client/go/cmd/api_key.go
blob: 5cc1dab8a35ee819d17f55bbc204abf6a940face (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
// vespa api-key command
// Author: mpolden
package cmd

import (
	"fmt"
	"io/ioutil"
	"log"

	"github.com/spf13/cobra"
	"github.com/vespa-engine/vespa/client/go/util"
	"github.com/vespa-engine/vespa/client/go/vespa"
)

var overwriteKey bool

const apiKeyLongDoc = `Create a new user API key for authentication with Vespa Cloud.

The API key will be stored in the Vespa CLI home directory
(see 'vespa help config'). Other commands will then automatically load the API
key as necessary.

It's possible to override the API key used through environment variables. This
can be useful in continuous integration systems.

* VESPA_CLI_API_KEY containing the key directly:

  export VESPA_CLI_API_KEY="my api key"

* VESPA_CLI_API_KEY_FILE containing path to the key:

  export VESPA_CLI_API_KEY_FILE=/path/to/api-key

Note that when overriding API key through environment variables, that key will
always be used. It's not possible to specify a tenant-specific key.`

func init() {
	apiKeyCmd.Flags().BoolVarP(&overwriteKey, "force", "f", false, "Force overwrite of existing API key")
	apiKeyCmd.MarkPersistentFlagRequired(applicationFlag)
	deprecatedApiKeyCmd.Flags().BoolVarP(&overwriteKey, "force", "f", false, "Force overwrite of existing API key")
	deprecatedApiKeyCmd.MarkPersistentFlagRequired(applicationFlag)
}

func apiKeyExample() string {
	return "$ vespa auth api-key -a my-tenant.my-app.my-instance"
}

var apiKeyCmd = &cobra.Command{
	Use:               "api-key",
	Short:             "Create a new user API key for authentication with Vespa Cloud",
	Long:              apiKeyLongDoc,
	Example:           apiKeyExample(),
	DisableAutoGenTag: true,
	SilenceUsage:      true,
	Args:              cobra.ExactArgs(0),
	RunE:              doApiKey,
}

var deprecatedApiKeyCmd = &cobra.Command{
	Use:               "api-key",
	Short:             "Create a new user API key for authentication with Vespa Cloud",
	Long:              apiKeyLongDoc,
	Example:           apiKeyExample(),
	DisableAutoGenTag: true,
	SilenceUsage:      true,
	Args:              cobra.ExactArgs(0),
	Hidden:            true,
	Deprecated:        "use 'vespa auth api-key' instead",
	RunE:              doApiKey,
}

func doApiKey(_ *cobra.Command, _ []string) error {
	cfg, err := LoadConfig()
	if err != nil {
		return fmt.Errorf("could not load config: %w", err)
	}
	app, err := getApplication()
	if err != nil {
		return err
	}
	apiKeyFile := cfg.APIKeyPath(app.Tenant)
	if util.PathExists(apiKeyFile) && !overwriteKey {
		err := fmt.Errorf("refusing to overwrite %s", apiKeyFile)
		printErrHint(err, "Use -f to overwrite it")
		printPublicKey(apiKeyFile, app.Tenant)
		return ErrCLI{error: err, quiet: true}
	}
	apiKey, err := vespa.CreateAPIKey()
	if err != nil {
		return fmt.Errorf("could not create api key: %w", err)
	}
	if err := ioutil.WriteFile(apiKeyFile, apiKey, 0600); err == nil {
		printSuccess("API private key written to ", apiKeyFile)
		return printPublicKey(apiKeyFile, app.Tenant)
	} else {
		return fmt.Errorf("failed to write: %s: %w", apiKeyFile, err)
	}
}

func printPublicKey(apiKeyFile, tenant string) error {
	pemKeyData, err := ioutil.ReadFile(apiKeyFile)
	if err != nil {
		return fmt.Errorf("failed to read: %s: %w", apiKeyFile, err)
	}
	key, err := vespa.ECPrivateKeyFrom(pemKeyData)
	if err != nil {
		return fmt.Errorf("failed to load key: %w", err)
	}
	pemPublicKey, err := vespa.PEMPublicKeyFrom(key)
	if err != nil {
		return fmt.Errorf("failed to extract public key: %w", err)
	}
	fingerprint, err := vespa.FingerprintMD5(pemPublicKey)
	if err != nil {
		return fmt.Errorf("failed to extract fingerprint: %w", err)
	}
	log.Printf("\nThis is your public key:\n%s", color.Green(pemPublicKey))
	log.Printf("Its fingerprint is:\n%s\n", color.Cyan(fingerprint))
	log.Print("\nTo use this key in Vespa Cloud click 'Add custom key' at")
	log.Printf(color.Cyan("%s/tenant/%s/keys").String(), getConsoleURL(), tenant)
	log.Print("and paste the entire public key including the BEGIN and END lines.")
	return nil
}