package auth import ( "context" "encoding/json" "fmt" "github.com/databricks/cli/cmd/root" "github.com/databricks/cli/libs/cmdio" "github.com/databricks/cli/libs/flags" "github.com/databricks/databricks-sdk-go/config" "github.com/spf13/cobra" ) var authTemplate = `{{"Host:" | bold}} {{.Status.Details.Host}} {{- if .Status.AccountID}} {{"Account ID:" | bold}} {{.Status.AccountID}} {{- end}} {{- if .Status.Username}} {{"User:" | bold}} {{.Status.Username}} {{- end}} {{"Authenticated with:" | bold}} {{.Status.Details.AuthType}} ----- ` + configurationTemplate var errorTemplate = `Unable to authenticate: {{.Status.Error}} ----- ` + configurationTemplate const configurationTemplate = `Current configuration: {{- $details := .Status.Details}} {{- range $a := .ConfigAttributes}} {{- $k := $a.Name}} {{- if index $details.Configuration $k}} {{- $v := index $details.Configuration $k}} {{if $v.AuthTypeMismatch}}~{{else}}✓{{end}} {{$k | bold}}: {{$v.Value}} {{- if not (eq $v.Source.String "dynamic configuration")}} {{- " (from" | italic}} {{$v.Source.String | italic}} {{- if $v.AuthTypeMismatch}}, {{ "not used for auth type " | red | italic }}{{$details.AuthType | red | italic}}{{end}}) {{- end}} {{- end}} {{- end}} ` func newDescribeCommand() *cobra.Command { cmd := &cobra.Command{ Use: "describe", Short: "Describes the credentials and the source of those credentials, being used by the CLI to authenticate", } var showSensitive bool cmd.Flags().BoolVar(&showSensitive, "sensitive", false, "Include sensitive fields like passwords and tokens in the output") cmd.RunE = func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() var status *authStatus var err error status, err = getAuthStatus(cmd, args, showSensitive, func(cmd *cobra.Command, args []string) (*config.Config, bool, error) { isAccount, err := root.MustAnyClient(cmd, args) return root.ConfigUsed(cmd.Context()), isAccount, err }) if err != nil { return err } if status.Error != nil { return render(ctx, cmd, status, errorTemplate) } return render(ctx, cmd, status, authTemplate) } return cmd } type tryAuth func(cmd *cobra.Command, args []string) (*config.Config, bool, error) func getAuthStatus(cmd *cobra.Command, args []string, showSensitive bool, fn tryAuth) (*authStatus, error) { cfg, isAccount, err := fn(cmd, args) ctx := cmd.Context() if err != nil { return &authStatus{ Status: "error", Error: err, Details: getAuthDetails(cmd, cfg, showSensitive), }, nil } if isAccount { a := root.AccountClient(ctx) // Doing a simple API call to check if the auth is valid _, err := a.Workspaces.List(ctx) if err != nil { return &authStatus{ Status: "error", Error: err, Details: getAuthDetails(cmd, cfg, showSensitive), }, nil } status := authStatus{ Status: "success", Details: getAuthDetails(cmd, a.Config, showSensitive), AccountID: a.Config.AccountID, Username: a.Config.Username, } return &status, nil } w := root.WorkspaceClient(ctx) me, err := w.CurrentUser.Me(ctx) if err != nil { return &authStatus{ Status: "error", Error: err, Details: getAuthDetails(cmd, cfg, showSensitive), }, nil } status := authStatus{ Status: "success", Details: getAuthDetails(cmd, w.Config, showSensitive), Username: me.UserName, } return &status, nil } func render(ctx context.Context, cmd *cobra.Command, status *authStatus, template string) error { switch root.OutputType(cmd) { case flags.OutputText: return cmdio.RenderWithTemplate(ctx, map[string]any{ "Status": status, "ConfigAttributes": config.ConfigAttributes, }, "", template) case flags.OutputJSON: buf, err := json.MarshalIndent(status, "", " ") if err != nil { return err } cmd.OutOrStdout().Write(buf) default: return fmt.Errorf("unknown output type %s", root.OutputType(cmd)) } return nil } type authStatus struct { Status string `json:"status"` Error error `json:"error,omitempty"` Username string `json:"username,omitempty"` AccountID string `json:"account_id,omitempty"` Details config.AuthDetails `json:"details"` } func getAuthDetails(cmd *cobra.Command, cfg *config.Config, showSensitive bool) config.AuthDetails { var opts []config.AuthDetailsOptions if showSensitive { opts = append(opts, config.ShowSensitive) } details := cfg.GetAuthDetails(opts...) for k, v := range details.Configuration { if k == "profile" && cmd.Flag("profile").Changed { v.Source = config.Source{Type: config.SourceType("flag"), Name: "--profile"} } if k == "host" && cmd.Flag("host").Changed { v.Source = config.Source{Type: config.SourceType("flag"), Name: "--host"} } } // If profile is not set explicitly, default to "default" if _, ok := details.Configuration["profile"]; !ok { profile := cfg.Profile if profile == "" { profile = "default" } details.Configuration["profile"] = &config.AttrConfig{Value: profile, Source: config.Source{Type: config.SourceDynamicConfig}} } // Unset source for databricks_cli_path because it can't be overridden anyway if v, ok := details.Configuration["databricks_cli_path"]; ok { v.Source = config.Source{Type: config.SourceDynamicConfig} } return details }