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

feat: config init command #291

Merged
merged 12 commits into from
Jun 14, 2021
7 changes: 2 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -58,19 +58,16 @@ mocks: $(GOBIN)/mockgen
$(GOBIN)/commander:
cd && GO111MODULE=auto go get github.com/commander-cli/commander/cmd/commander

$(GOBIN)/auth0-cli-config-generator:
go install ./pkg/auth0-cli-config-generator

run-integration:
auth0-cli-config-generator && commander test commander.yaml
auth0 config init && commander test commander.yaml
.PHONY: run-integration

# Delete all test apps created during integration testing
integration-cleanup:
./integration/test-cleanup.sh
.PHONY: integration-cleanup

integration: build $(GOBIN)/auth0-cli-config-generator $(GOBIN)/commander
integration: build $(GOBIN)/commander
$(MAKE) run-integration; \
ret=$$?; \
$(MAKE) integration-cleanup; \
Expand Down
8 changes: 8 additions & 0 deletions internal/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,14 @@ func (c *cli) initContext() (err error) {
return nil
}

func (c *cli) setPath(p string) {
if p == "" {
c.path = path.Join(os.Getenv("HOME"), ".config", "auth0", "config.json")
return
}
c.path = p
}

func canPrompt(cmd *cobra.Command) bool {
noInput, err := cmd.Root().Flags().GetBool("no-input")

Expand Down
137 changes: 137 additions & 0 deletions internal/cli/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package cli

import (
"context"
"fmt"
"net/url"
"os"
"path"
"strings"

"github.com/auth0/auth0-cli/internal/auth"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"golang.org/x/oauth2/clientcredentials"
)

var requiredScopes = auth.RequiredScopes()

var desiredInputs = `Config init is intended for non-interactive use,
ensure the following env variables are set:

AUTH0_CLI_CLIENT_DOMAIN
AUTH0_CLI_CLIENT_ID
AUTH0_CLI_CLIENT_SECRET

Interactive logins should use "auth0 login" instead.`

type params struct {
filePath string
clientDomain string
clientID string
clientSecret string
}

func (p params) validate() error {
as-herzog marked this conversation as resolved.
Show resolved Hide resolved
if p.clientDomain == "" {
return fmt.Errorf("Missing client domain.\n%s", desiredInputs)
}

u, err := url.Parse(p.clientDomain)
if err != nil {
return fmt.Errorf("Failed to parse client domain: %s", p.clientDomain)
}

if u.Scheme != "" {
return fmt.Errorf("Client domain cant include a scheme: %s", p.clientDomain)
}

if p.clientID == "" {
return fmt.Errorf("Missing client id.\n%s", desiredInputs)
}

if p.clientSecret == "" {
return fmt.Errorf("Missing client secret.\n%s", desiredInputs)
}
return nil
}

func configCmd(cli *cli) *cobra.Command {
cmd := &cobra.Command{
Use: "config",
Short: "Manage auth0-cli config",
Long: "Manage auth0-cli config",
}

cmd.AddCommand(initCmd(cli))
return cmd
}

func initCmd(cli *cli) *cobra.Command {
cmd := &cobra.Command{
Use: "init",
Short: "initialize valid cli config from environment variables",
as-herzog marked this conversation as resolved.
Show resolved Hide resolved
RunE: func(command *cobra.Command, args []string) error {
filePath := viper.GetString("FILEPATH")
clientDomain := viper.GetString("CLIENT_DOMAIN")
clientID := viper.GetString("CLIENT_ID")
clientSecret := viper.GetString("CLIENT_SECRET")

cli.setPath(filePath)
p := params{filePath, clientDomain, clientID, clientSecret}
if err := p.validate(); err != nil {
return err
}

u, err := url.Parse("https://" + p.clientDomain)
if err != nil {
return err
}

// integration test client doesn't have openid or offline_access scopes granted
scopesForTest := requiredScopes[2:]
as-herzog marked this conversation as resolved.
Show resolved Hide resolved
c := &clientcredentials.Config{
ClientID: p.clientID,
ClientSecret: p.clientSecret,
TokenURL: u.String() + "/oauth/token",
EndpointParams: url.Values{
"client_id": {p.clientID},
"scope": {strings.Join(scopesForTest, " ")},
"audience": {u.String() + "/api/v2/"},
},
}

token, err := c.Token(context.Background())
if err != nil {
return err
}

t := tenant{
Name: p.clientDomain,
Domain: p.clientDomain,
AccessToken: token.AccessToken,
ExpiresAt: token.Expiry,
Scopes: requiredScopes,
}

if err := cli.addTenant(t); err != nil {
return fmt.Errorf("Unexpected error adding tenant to config: %w", err)
}
return nil
},
}
viper.SetEnvPrefix("AUTH0_CLI")
viper.AutomaticEnv()

flags := cmd.Flags()
flags.String("filepath", path.Join(os.Getenv("HOME"), ".config", "auth0", "config.json"), "Filepath for the auth0 cli config")
as-herzog marked this conversation as resolved.
Show resolved Hide resolved
_ = viper.BindPFlag("FILEPATH", flags.Lookup("filepath"))
flags.String("client-id", "", "Client ID to set within config")
_ = viper.BindPFlag("CLIENT_ID", flags.Lookup("client-id"))
flags.String("client-secret", "", "Client secret to use to generate token which is set within config")
_ = viper.BindPFlag("CLIENT_SECRET", flags.Lookup("client-secret"))
flags.String("client-domain", "", "Client domain to use to generate token which is set within config")
_ = viper.BindPFlag("CLIENT_DOMAIN", flags.Lookup("client-domain"))

return cmd
}
6 changes: 6 additions & 0 deletions internal/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ func Execute() {
return nil
}

// config init shouldn't trigger a login.
if cmd.CalledAs() == "init" && cmd.Parent().Use == "config" {
return nil
}

// Initialize everything once. Later callers can then
// freely assume that config is fully primed and ready
// to go.
Expand Down Expand Up @@ -90,6 +95,7 @@ func Execute() {
// order of the comamnds here matters
// so add new commands in a place that reflect its relevance or relation with other commands:
rootCmd.AddCommand(loginCmd(cli))
rootCmd.AddCommand(configCmd(cli))
rootCmd.AddCommand(tenantsCmd(cli))
rootCmd.AddCommand(usersCmd(cli))
rootCmd.AddCommand(appsCmd(cli))
Expand Down
Loading