Repository: Versent/saml2aws
Pull request #108 (2017-12-11 23:21:18)

Feature/command specific help
Bit of internal refactoring of how flags are handled, but (hopefully) the only user-visible change should be updated `--help-long` output showing which flags are command-specific (vs common to all commands)


Patch:
diff --git a/.gitignore b/.gitignore
index 11b4ea5..9e601d6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+.DS_Store
 .credentials
 dist
 build
diff --git a/README.md b/README.md
index 4e7310a..e02266b 100644
--- a/README.md
+++ b/README.md
@@ -39,19 +39,18 @@ A command line tool to help with SAML access to the AWS token service.
 
 Flags:
       --help                   Show context-sensitive help (also try --help-long and --help-man).
-      --verbose                Enable verbose logging
       --version                Show application version.
+      --verbose                Enable verbose logging
+  -i, --provider=PROVIDER      This flag it is obsolete see https://github.com/Versent/saml2aws#adding-idp-accounts.
   -a, --idp-account="default"  The name of the configured IDP account
       --idp-provider=IDP-PROVIDER
                                The configured IDP provider
       --mfa="Auto"             The name of the mfa
-  -p, --profile="saml"         The AWS profile to save the temporary credentials
   -s, --skip-verify            Skip verification of server certificate.
-  -i, --provider=PROVIDER      This flag it is obsolete see https://github.com/Versent/saml2aws#adding-idp-accounts.
       --url=URL                The URL of the SAML IDP server used to login.
       --username=USERNAME      The username used to login.
-      --password=PASSWORD      The password used to login.
       --role=ROLE              The ARN of the role to assume.
+      --aws-urn=AWS-URN        The URN used by SAML when you login.
       --skip-prompt            Skip prompting for parameters during login.
 
 Commands:
@@ -59,16 +58,21 @@ Commands:
     Show help.
 
 
-  login
+  configure
+    Configure a new IDP account.
+
+
+  login [<flags>]
     Login to a SAML 2.0 IDP and convert the SAML assertion to an STS token.
 
+        --password=PASSWORD  The password used to login.
+    -p, --profile="saml"     The AWS profile to save the temporary credentials
 
-  exec [<command>...]
+  exec [<flags>] [<command>...]
     Exec the supplied command with env vars from STS token.
 
-
-  configure
-    Configure a new IDP account.
+        --password=PASSWORD  The password used to login.
+    -p, --profile="saml"     The AWS profile to save the temporary credentials
 
 ```
 
diff --git a/cmd/saml2aws/commands/configure.go b/cmd/saml2aws/commands/configure.go
index d1525aa..0c708af 100644
--- a/cmd/saml2aws/commands/configure.go
+++ b/cmd/saml2aws/commands/configure.go
@@ -6,12 +6,13 @@ import (
 	"github.com/pkg/errors"
 	"github.com/versent/saml2aws"
 	"github.com/versent/saml2aws/pkg/cfg"
+	"github.com/versent/saml2aws/pkg/flags"
 )
 
 // Configure configure account profiles
-func Configure(loginFlags *LoginFlags, cmdline []string) error {
+func Configure(configFlags *flags.CommonFlags) error {
 
-	idpAccountName := loginFlags.IdpAccount
+	idpAccountName := configFlags.IdpAccount
 
 	cfgm, err := cfg.NewConfigManager(cfg.DefaultConfigPath)
 	if err != nil {
@@ -24,10 +25,10 @@ func Configure(loginFlags *LoginFlags, cmdline []string) error {
 	}
 
 	// update username and hostname if supplied
-	applyFlagOverrides(loginFlags, account)
+	flags.ApplyFlagOverrides(configFlags, account)
 
 	// do we need to prompt for values now?
-	if !loginFlags.SkipPrompt {
+	if !configFlags.SkipPrompt {
 		err = saml2aws.PromptForConfigurationDetails(account)
 		if err != nil {
 			return errors.Wrap(err, "failed to input configuration")
diff --git a/cmd/saml2aws/commands/exec.go b/cmd/saml2aws/commands/exec.go
index a00d8f8..119cac5 100644
--- a/cmd/saml2aws/commands/exec.go
+++ b/cmd/saml2aws/commands/exec.go
@@ -10,17 +10,18 @@ import (
 	"github.com/aws/aws-sdk-go/service/sts"
 	"github.com/pkg/errors"
 	"github.com/versent/saml2aws/pkg/awsconfig"
+	"github.com/versent/saml2aws/pkg/flags"
 	"github.com/versent/saml2aws/pkg/shell"
 )
 
 // Exec execute the supplied command after seeding the environment
-func Exec(loginFlags *LoginFlags, cmdline []string) error {
+func Exec(execFlags *flags.LoginExecFlags, cmdline []string) error {
 
 	if len(cmdline) < 1 {
 		return fmt.Errorf("Command to execute required")
 	}
 
-	sharedCreds := awsconfig.NewSharedCredentials(loginFlags.Profile)
+	sharedCreds := awsconfig.NewSharedCredentials(execFlags.Profile)
 
 	// this checks if the credentials file has been created yet
 	// can only really be triggered if saml2aws exec is run on a new
@@ -34,13 +35,13 @@ func Exec(loginFlags *LoginFlags, cmdline []string) error {
 		return nil
 	}
 
-	ok, err := checkToken(loginFlags.Profile)
+	ok, err := checkToken(execFlags.Profile)
 	if err != nil {
 		return errors.Wrap(err, "error validating token")
 	}
 
 	if !ok {
-		err = Login(loginFlags)
+		err = Login(execFlags)
 	}
 	if err != nil {
 		return errors.Wrap(err, "error logging in")
diff --git a/cmd/saml2aws/commands/login.go b/cmd/saml2aws/commands/login.go
index 5acc801..05cd067 100644
--- a/cmd/saml2aws/commands/login.go
+++ b/cmd/saml2aws/commands/login.go
@@ -15,35 +15,14 @@ import (
 	"github.com/versent/saml2aws/pkg/awsconfig"
 	"github.com/versent/saml2aws/pkg/cfg"
 	"github.com/versent/saml2aws/pkg/creds"
+	"github.com/versent/saml2aws/pkg/flags"
 )
 
 // MaxDurationSeconds the maximum duration in seconds for an STS session
 const MaxDurationSeconds = 3600
 
-// LoginFlags login specific command flags
-type LoginFlags struct {
-	IdpAccount           string
-	IdpProvider          string
-	MFA                  string
-	Profile              string
-	URL                  string
-	Username             string
-	Password             string
-	RoleArn              string
-	AmazonWebservicesURN string
-	SkipVerify           bool
-	Timeout              int
-	SkipPrompt           bool
-	Provider             string
-}
-
-// RoleSupplied role arn has been passed as a flag
-func (lf *LoginFlags) RoleSupplied() bool {
-	return lf.RoleArn != ""
-}
-
 // Login login to ADFS
-func Login(loginFlags *LoginFlags) error {
+func Login(loginFlags *flags.LoginExecFlags) error {
 
 	logger := logrus.WithField("command", "login")
 
@@ -125,13 +104,13 @@ func Login(loginFlags *LoginFlags) error {
 	return nil
 }
 
-func buildIdpAccount(loginFlags *LoginFlags) (*cfg.IDPAccount, error) {
+func buildIdpAccount(loginFlags *flags.LoginExecFlags) (*cfg.IDPAccount, error) {
 	cfgm, err := cfg.NewConfigManager(cfg.DefaultConfigPath)
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to load configuration")
 	}
 
-	account, err := cfgm.LoadVerifyIDPAccount(loginFlags.IdpAccount)
+	account, err := cfgm.LoadVerifyIDPAccount(loginFlags.CommonFlags.IdpAccount)
 	if err != nil {
 		if cfg.IsErrIdpAccountNotFound(err) {
 			fmt.Printf("%v\n", err)
@@ -141,7 +120,7 @@ func buildIdpAccount(loginFlags *LoginFlags) (*cfg.IDPAccount, error) {
 	}
 
 	// update username and hostname if supplied
-	applyFlagOverrides(loginFlags, account)
+	flags.ApplyFlagOverrides(loginFlags.CommonFlags, account)
 
 	err = account.Validate()
 	if err != nil {
@@ -151,13 +130,13 @@ func buildIdpAccount(loginFlags *LoginFlags) (*cfg.IDPAccount, error) {
 	return account, nil
 }
 
-func resolveLoginDetails(account *cfg.IDPAccount, loginFlags *LoginFlags) (*creds.LoginDetails, error) {
+func resolveLoginDetails(account *cfg.IDPAccount, loginFlags *flags.LoginExecFlags) (*creds.LoginDetails, error) {
 
 	// fmt.Printf("loginFlags %+v\n", loginFlags)
 
 	loginDetails := &creds.LoginDetails{URL: account.URL, Username: account.Username}
 
-	fmt.Printf("Using IDP Account %s to access %s %s\n", loginFlags.IdpAccount, account.Provider, account.URL)
+	fmt.Printf("Using IDP Account %s to access %s %s\n", loginFlags.CommonFlags.IdpAccount, account.Provider, account.URL)
 
 	err := credentials.LookupCredentials(loginDetails)
 	if err != nil {
@@ -169,8 +148,8 @@ func resolveLoginDetails(account *cfg.IDPAccount, loginFlags *LoginFlags) (*cred
 	// fmt.Printf("%s %s\n", savedUsername, savedPassword)
 
 	// if you supply a username in a flag it takes precedence
-	if loginFlags.Username != "" {
-		loginDetails.Username = loginFlags.Username
+	if loginFlags.CommonFlags.Username != "" {
+		loginDetails.Username = loginFlags.CommonFlags.Username
 	}
 
 	// if you supply a password in a flag it takes precedence
@@ -181,7 +160,7 @@ func resolveLoginDetails(account *cfg.IDPAccount, loginFlags *LoginFlags) (*cred
 	// fmt.Printf("loginDetails %+v\n", loginDetails)
 
 	// if skip prompt was passed just pass back the flag values
-	if loginFlags.SkipPrompt {
+	if loginFlags.CommonFlags.SkipPrompt {
 		return loginDetails, nil
 	}
 
@@ -193,12 +172,12 @@ func resolveLoginDetails(account *cfg.IDPAccount, loginFlags *LoginFlags) (*cred
 	return loginDetails, nil
 }
 
-func resolveRole(awsRoles []*saml2aws.AWSRole, samlAssertion string, loginFlags *LoginFlags) (*saml2aws.AWSRole, error) {
+func resolveRole(awsRoles []*saml2aws.AWSRole, samlAssertion string, loginFlags *flags.LoginExecFlags) (*saml2aws.AWSRole, error) {
 	var role = new(saml2aws.AWSRole)
 
 	if len(awsRoles) == 1 {
-		if loginFlags.RoleSupplied() {
-			return saml2aws.LocateRole(awsRoles, loginFlags.RoleArn)
+		if loginFlags.CommonFlags.RoleSupplied() {
+			return saml2aws.LocateRole(awsRoles, loginFlags.CommonFlags.RoleArn)
 		}
 		return awsRoles[0], nil
 	} else if len(awsRoles) == 0 {
@@ -212,8 +191,8 @@ func resolveRole(awsRoles []*saml2aws.AWSRole, samlAssertion string, loginFlags
 
 	saml2aws.AssignPrincipals(awsRoles, awsAccounts)
 
-	if loginFlags.RoleSupplied() {
-		return saml2aws.LocateRole(awsRoles, loginFlags.RoleArn)
+	if loginFlags.CommonFlags.RoleSupplied() {
+		return saml2aws.LocateRole(awsRoles, loginFlags.CommonFlags.RoleArn)
 	}
 
 	for {
@@ -267,33 +246,3 @@ func loginToStsUsingRole(role *saml2aws.AWSRole, samlAssertion string, profile s
 
 	return nil
 }
-
-func applyFlagOverrides(loginFlags *LoginFlags, account *cfg.IDPAccount) {
-	if loginFlags.URL != "" {
-		account.URL = loginFlags.URL
-	}
-
-	if loginFlags.Username != "" {
-		account.Username = loginFlags.Username
-	}
-
-	if loginFlags.SkipVerify {
-		account.SkipVerify = loginFlags.SkipVerify
-	}
-
-	if loginFlags.IdpProvider != "" {
-		account.Provider = loginFlags.IdpProvider
-	}
-
-	if loginFlags.MFA != "" {
-		account.MFA = loginFlags.MFA
-	}
-
-	if loginFlags.AmazonWebservicesURN != "" {
-		account.AmazonWebservicesURN = loginFlags.AmazonWebservicesURN
-	}
-
-	if loginFlags.Timeout > 0 {
-		account.Timeout = loginFlags.Timeout
-	}
-}
diff --git a/cmd/saml2aws/main.go b/cmd/saml2aws/main.go
index e50d0ca..e78f8e3 100644
--- a/cmd/saml2aws/main.go
+++ b/cmd/saml2aws/main.go
@@ -7,22 +7,16 @@ import (
 	"github.com/alecthomas/kingpin"
 	"github.com/sirupsen/logrus"
 	"github.com/versent/saml2aws/cmd/saml2aws/commands"
+	"github.com/versent/saml2aws/pkg/flags"
 )
 
 var (
-	app = kingpin.New("saml2aws", "A command line tool to help with SAML access to the AWS token service.")
-
-	verbose = app.Flag("verbose", "Enable verbose logging").Bool()
-
-	cmdLogin     = app.Command("login", "Login to a SAML 2.0 IDP and convert the SAML assertion to an STS token.")
-	cmdExec      = app.Command("exec", "Exec the supplied command with env vars from STS token.")
-	cmdConfigure = app.Command("configure", "Configure a new IDP account.")
-	cmdLine      = buildCmdList(cmdExec.Arg("command", "The command to execute."))
-
 	// Version app version
 	Version = "1.0.0"
 )
 
+// The `cmdLineList` type is used to make a `[]string` meet the requiements
+// of the kingpin.Value interface
 type cmdLineList []string
 
 func (i *cmdLineList) Set(value string) error {
@@ -45,58 +39,69 @@ func buildCmdList(s kingpin.Settings) (target *[]string) {
 	return
 }
 
-func configureLoginFlags(app *kingpin.Application) *commands.LoginFlags {
-	c := &commands.LoginFlags{}
-
-	app.Flag("idp-account", "The name of the configured IDP account").Short('a').Default("default").StringVar(&c.IdpAccount)
-	app.Flag("idp-provider", "The configured IDP provider").EnumVar(&c.IdpProvider, "ADFS", "ADFS2", "Ping", "JumpCloud", "Okta", "KeyCloak")
-	app.Flag("mfa", "The name of the mfa").Default("Auto").StringVar(&c.MFA)
-	app.Flag("profile", "The AWS profile to save the temporary credentials").Short('p').Default("saml").StringVar(&c.Profile)
-	app.Flag("skip-verify", "Skip verification of server certificate.").Short('s').BoolVar(&c.SkipVerify)
-	// app.Flag("timeout", "Override the default HTTP client timeout in seconds.").Short('t').IntVar(&c.Timeout)
-
-	// using this flag to highlight the
-	app.Flag("provider", "This flag it is obsolete see https://github.com/Versent/saml2aws#adding-idp-accounts.").Short('i').EnumVar(&c.Provider, "ADFS", "ADFS2", "Ping", "JumpCloud", "Okta", "KeyCloak")
-
-	app.Flag("url", "The URL of the SAML IDP server used to login.").StringVar(&c.URL)
-	app.Flag("username", "The username used to login.").StringVar(&c.Username)
-	app.Flag("password", "The password used to login.").Envar("SAML2AWS_PASSWORD").StringVar(&c.Password)
-	app.Flag("role", "The ARN of the role to assume.").StringVar(&c.RoleArn)
-	app.Flag("aws-urn", "The URN used by SAML when you login.").StringVar(&c.AmazonWebservicesURN)
-	app.Flag("skip-prompt", "Skip prompting for parameters during login.").BoolVar(&c.SkipPrompt)
-
-	return c
-}
-
 func main() {
 
+	app := kingpin.New("saml2aws", "A command line tool to help with SAML access to the AWS token service.")
 	app.Version(Version)
 
-	lc := configureLoginFlags(app)
-
+	// Settings not related to commands
+	verbose := app.Flag("verbose", "Enable verbose logging").Bool()
+	provider := app.Flag("provider", "This flag it is obsolete see https://github.com/Versent/saml2aws#adding-idp-accounts.").Short('i').Enum("ADFS", "ADFS2", "Ping", "JumpCloud", "Okta", "KeyCloak")
+
+	// Common (to all commands) settings
+	commonFlags := new(flags.CommonFlags)
+	app.Flag("idp-account", "The name of the configured IDP account").Short('a').Default("default").StringVar(&commonFlags.IdpAccount)
+	app.Flag("idp-provider", "The configured IDP provider").EnumVar(&commonFlags.IdpProvider, "ADFS", "ADFS2", "Ping", "JumpCloud", "Okta", "KeyCloak")
+	app.Flag("mfa", "The name of the mfa").Default("Auto").StringVar(&commonFlags.MFA)
+	app.Flag("skip-verify", "Skip verification of server certificate.").Short('s').BoolVar(&commonFlags.SkipVerify)
+	app.Flag("url", "The URL of the SAML IDP server used to login.").StringVar(&commonFlags.URL)
+	app.Flag("username", "The username used to login.").Envar("SAML2AWS_USERNAME").StringVar(&commonFlags.Username)
+	app.Flag("role", "The ARN of the role to assume.").StringVar(&commonFlags.RoleArn)
+	app.Flag("aws-urn", "The URN used by SAML when you login.").StringVar(&commonFlags.AmazonWebservicesURN)
+	app.Flag("skip-prompt", "Skip prompting for parameters during login.").BoolVar(&commonFlags.SkipPrompt)
+
+	// `configure` command and settings
+	cmdConfigure := app.Command("configure", "Configure a new IDP account.")
+	configFlags := commonFlags
+
+	// `login` command and settings
+	cmdLogin := app.Command("login", "Login to a SAML 2.0 IDP and convert the SAML assertion to an STS token.")
+	loginFlags := new(flags.LoginExecFlags)
+	loginFlags.CommonFlags = commonFlags
+	cmdLogin.Flag("password", "The password used to login.").Envar("SAML2AWS_PASSWORD").StringVar(&loginFlags.Password)
+	cmdLogin.Flag("profile", "The AWS profile to save the temporary credentials").Short('p').Default("saml").StringVar(&loginFlags.Profile)
+
+	// `exec` command and settings
+	cmdExec := app.Command("exec", "Exec the supplied command with env vars from STS token.")
+	execFlags := new(flags.LoginExecFlags)
+	execFlags.CommonFlags = commonFlags
+	cmdExec.Flag("password", "The password used to login.").Envar("SAML2AWS_PASSWORD").StringVar(&loginFlags.Password)
+	cmdExec.Flag("profile", "The AWS profile to save the temporary credentials").Short('p').Default("saml").StringVar(&loginFlags.Profile)
+	cmdLine := buildCmdList(cmdExec.Arg("command", "The command to execute."))
+
+	// Trigger the parsing of the command line inputs via kingpin
 	command := kingpin.MustParse(app.Parse(os.Args[1:]))
 
-	if *verbose {
-		logrus.SetLevel(logrus.DebugLevel)
-	}
-
 	// will leave this here for a while during upgrade process
-	if lc.Provider != "" {
+	if *provider != "" {
 		fmt.Println("The --provider flag has been replaced with a new configure command. See https://github.com/Versent/saml2aws#adding-idp-accounts")
 		os.Exit(1)
 	}
 
-	var err error
+	if *verbose {
+		logrus.SetLevel(logrus.DebugLevel)
+	}
 
 	logrus.WithField("command", command).Debug("Running")
 
+	var err error
 	switch command {
 	case cmdLogin.FullCommand():
-		err = commands.Login(lc)
+		err = commands.Login(loginFlags)
 	case cmdExec.FullCommand():
-		err = commands.Exec(lc, *cmdLine)
+		err = commands.Exec(execFlags, *cmdLine)
 	case cmdConfigure.FullCommand():
-		err = commands.Configure(lc, *cmdLine)
+		err = commands.Configure(configFlags)
 	}
 
 	if err != nil {
diff --git a/pkg/flags/flags.go b/pkg/flags/flags.go
new file mode 100644
index 0000000..82f3d9f
--- /dev/null
+++ b/pkg/flags/flags.go
@@ -0,0 +1,55 @@
+package flags
+
+import "github.com/versent/saml2aws/pkg/cfg"
+
+// CommonFlags flags common to all of the `saml2aws` commands (except `help`)
+type CommonFlags struct {
+	IdpAccount           string
+	IdpProvider          string
+	MFA                  string
+	URL                  string
+	Username             string
+	RoleArn              string
+	AmazonWebservicesURN string
+	SkipPrompt           bool
+	SkipVerify           bool
+}
+
+// RoleSupplied role arn has been passed as a flag
+func (cf *CommonFlags) RoleSupplied() bool {
+	return cf.RoleArn != ""
+}
+
+// LoginExecFlags flags for the Login / Exec commands
+type LoginExecFlags struct {
+	CommonFlags *CommonFlags
+	Profile     string
+	Password    string
+}
+
+// ApplyFlagOverrides overrides IDPAccount with command line settings
+func ApplyFlagOverrides(commonFlags *CommonFlags, account *cfg.IDPAccount) {
+	if commonFlags.URL != "" {
+		account.URL = commonFlags.URL
+	}
+
+	if commonFlags.Username != "" {
+		account.Username = commonFlags.Username
+	}
+
+	if commonFlags.SkipVerify {
+		account.SkipVerify = commonFlags.SkipVerify
+	}
+
+	if commonFlags.IdpProvider != "" {
+		account.Provider = commonFlags.IdpProvider
+	}
+
+	if commonFlags.MFA != "" {
+		account.MFA = commonFlags.MFA
+	}
+
+	if commonFlags.AmazonWebservicesURN != "" {
+		account.AmazonWebservicesURN = commonFlags.AmazonWebservicesURN
+	}
+}


Test patch:
diff --git a/cmd/saml2aws/commands/login_test.go b/cmd/saml2aws/commands/login_test.go
index 4261af9..80a05c2 100644
--- a/cmd/saml2aws/commands/login_test.go
+++ b/cmd/saml2aws/commands/login_test.go
@@ -7,11 +7,13 @@ import (
 	"github.com/versent/saml2aws"
 	"github.com/versent/saml2aws/pkg/cfg"
 	"github.com/versent/saml2aws/pkg/creds"
+	"github.com/versent/saml2aws/pkg/flags"
 )
 
 func TestResolveLoginDetailsWithFlags(t *testing.T) {
 
-	loginFlags := &LoginFlags{URL: "https://id.example.com", Username: "wolfeidau", Password: "testtestlol", SkipPrompt: true}
+	commonFlags := &flags.CommonFlags{URL: "https://id.example.com", Username: "wolfeidau", SkipPrompt: true}
+	loginFlags := &flags.LoginExecFlags{CommonFlags: commonFlags, Password: "testtestlol"}
 
 	idpa := &cfg.IDPAccount{
 		URL:      "https://id.example.com",
@@ -37,7 +39,7 @@ func TestResolveRoleSingleEntry(t *testing.T) {
 		adminRole,
 	}
 
-	got, err := resolveRole(awsRoles, "", &LoginFlags{})
+	got, err := resolveRole(awsRoles, "", &flags.LoginExecFlags{CommonFlags: &flags.CommonFlags{}})
 	assert.Empty(t, err)
 	assert.Equal(t, got, adminRole)
 }
diff --git a/pkg/flags/flags_test.go b/pkg/flags/flags_test.go
new file mode 100644
index 0000000..e199b4b
--- /dev/null
+++ b/pkg/flags/flags_test.go
@@ -0,0 +1,92 @@
+package flags
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/versent/saml2aws/pkg/cfg"
+)
+
+func TestRoleSuppliedIsTrueWithNonEmptyRole(t *testing.T) {
+
+	commonFlags := &CommonFlags{RoleArn: "arn:aws:iam::456456456456/role:myrole"}
+
+	expected := true
+	actual := commonFlags.RoleSupplied()
+
+	assert.Equal(t, expected, actual)
+}
+
+func TestRoleSuppliedIsFalseWithEmptyRole(t *testing.T) {
+
+	commonFlags := &CommonFlags{RoleArn: ""}
+
+	expected := false
+	actual := commonFlags.RoleSupplied()
+
+	assert.Equal(t, expected, actual)
+}
+
+func TestOverrideAllFlags(t *testing.T) {
+
+	commonFlags := &CommonFlags{
+		IdpProvider:          "ADFS",
+		MFA:                  "mymfa",
+		SkipVerify:           true,
+		URL:                  "https://id.example.com",
+		Username:             "myuser",
+		AmazonWebservicesURN: "urn:amazon:webservices",
+	}
+	idpa := &cfg.IDPAccount{
+		Provider:             "Ping",
+		MFA:                  "none",
+		SkipVerify:           false,
+		URL:                  "https://id.test.com",
+		Username:             "test123",
+		AmazonWebservicesURN: "urn:govcloud:webservices",
+	}
+
+	expected := &cfg.IDPAccount{
+		Provider:             "ADFS",
+		MFA:                  "mymfa",
+		SkipVerify:           true,
+		URL:                  "https://id.example.com",
+		Username:             "myuser",
+		AmazonWebservicesURN: "urn:amazon:webservices",
+	}
+	ApplyFlagOverrides(commonFlags, idpa)
+
+	assert.Equal(t, expected, idpa)
+}
+
+func TestNoOverrides(t *testing.T) {
+
+	commonFlags := &CommonFlags{
+		IdpProvider:          "",
+		MFA:                  "",
+		SkipVerify:           false,
+		URL:                  "",
+		Username:             "",
+		AmazonWebservicesURN: "",
+	}
+	idpa := &cfg.IDPAccount{
+		Provider:             "Ping",
+		MFA:                  "none",
+		SkipVerify:           false,
+		URL:                  "https://id.test.com",
+		Username:             "test123",
+		AmazonWebservicesURN: "urn:govcloud:webservices",
+	}
+
+	expected := &cfg.IDPAccount{
+		Provider:             "Ping",
+		MFA:                  "none",
+		SkipVerify:           false,
+		URL:                  "https://id.test.com",
+		Username:             "test123",
+		AmazonWebservicesURN: "urn:govcloud:webservices",
+	}
+	ApplyFlagOverrides(commonFlags, idpa)
+
+	assert.Equal(t, expected, idpa)
+}