opentofu/internal/command/logout.go
namgyalangmo cb2e9119aa
Update copyright notice (#1232)
Signed-off-by: namgyalangmo <75657887+namgyalangmo@users.noreply.github.com>
2024-02-08 09:48:59 +00:00

156 lines
4.6 KiB
Go

// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package command
import (
"fmt"
"path/filepath"
"strings"
svchost "github.com/hashicorp/terraform-svchost"
"github.com/opentofu/opentofu/internal/command/cliconfig"
"github.com/opentofu/opentofu/internal/tfdiags"
)
// LogoutCommand is a Command implementation which removes stored credentials
// for a remote service host.
type LogoutCommand struct {
Meta
}
// Run implements cli.Command.
func (c *LogoutCommand) Run(args []string) int {
args = c.Meta.process(args)
cmdFlags := c.Meta.defaultFlagSet("logout")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
return 1
}
args = cmdFlags.Args()
if len(args) != 1 {
c.Ui.Error(
"The logout command expects exactly one argument: the host to log out of.")
cmdFlags.Usage()
return 1
}
var diags tfdiags.Diagnostics
givenHostname := args[0]
hostname, err := svchost.ForComparison(givenHostname)
if err != nil {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Invalid hostname",
fmt.Sprintf("The given hostname %q is not valid: %s.", givenHostname, err.Error()),
))
c.showDiagnostics(diags)
return 1
}
// From now on, since we've validated the given hostname, we should use
// dispHostname in the UI to ensure we're presenting it in the canonical
// form, in case that helps users with debugging when things aren't
// working as expected. (Perhaps the normalization is part of the cause.)
dispHostname := hostname.ForDisplay()
creds := c.Services.CredentialsSource().(*cliconfig.CredentialsSource)
filename, _ := creds.CredentialsFilePath()
credsCtx := &loginCredentialsContext{
Location: creds.HostCredentialsLocation(hostname),
LocalFilename: filename, // empty in the very unlikely event that we can't select a config directory for this user
HelperType: creds.CredentialsHelperType(),
}
if credsCtx.Location == cliconfig.CredentialsInOtherFile {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
fmt.Sprintf("Credentials for %s are manually configured", dispHostname),
"The \"tofu logout\" command cannot log out because credentials for this host are manually configured in a CLI configuration file.\n\nTo log out, revoke the existing credentials and remove that block from the CLI configuration.",
))
}
if diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}
switch credsCtx.Location {
case cliconfig.CredentialsNotAvailable:
c.Ui.Output(fmt.Sprintf("No credentials for %s are stored.\n", dispHostname))
return 0
case cliconfig.CredentialsViaHelper:
c.Ui.Output(fmt.Sprintf("Removing the stored credentials for %s from the configured\n%q credentials helper.\n", dispHostname, credsCtx.HelperType))
case cliconfig.CredentialsInPrimaryFile:
c.Ui.Output(fmt.Sprintf("Removing the stored credentials for %s from the following file:\n %s\n", dispHostname, credsCtx.LocalFilename))
}
err = creds.ForgetForHost(hostname)
if err != nil {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Failed to remove API token",
fmt.Sprintf("Unable to remove stored API token: %s", err),
))
}
c.showDiagnostics(diags)
if diags.HasErrors() {
return 1
}
c.Ui.Output(
fmt.Sprintf(
c.Colorize().Color(strings.TrimSpace(`
[green][bold]Success![reset] [bold]OpenTofu has removed the stored API token for %s.[reset]
`)),
dispHostname,
) + "\n",
)
return 0
}
// Help implements cli.Command.
func (c *LogoutCommand) Help() string {
defaultFile := c.defaultOutputFile()
if defaultFile == "" {
// Because this is just for the help message and it's very unlikely
// that a user wouldn't have a functioning home directory anyway,
// we'll just use a placeholder here. The real command has some
// more complex behavior for this case. This result is not correct
// on all platforms, but given how unlikely we are to hit this case
// that seems okay.
defaultFile = "~/.terraform/credentials.tfrc.json"
}
helpText := `
Usage: tofu [global options] logout [hostname]
Removes locally-stored credentials for specified hostname.
Note: the API token is only removed from local storage, not destroyed on the
remote server, so it will remain valid until manually revoked.
%s
`
return strings.TrimSpace(helpText)
}
// Synopsis implements cli.Command.
func (c *LogoutCommand) Synopsis() string {
return "Remove locally-stored credentials for a remote host"
}
func (c *LogoutCommand) defaultOutputFile() string {
if c.CLIConfigDir == "" {
return "" // no default available
}
return filepath.Join(c.CLIConfigDir, "credentials.tfrc.json")
}