diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml new file mode 100644 index 0000000..e55a4fc --- /dev/null +++ b/.github/workflows/lint.yaml @@ -0,0 +1,23 @@ +name: golangci-lint +on: + push: + branches: + - master + - main + pull_request: +permissions: + contents: read +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/setup-go@v3 + with: + go-version: 1.17 + - uses: actions/checkout@v3 + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version + version: latest \ No newline at end of file diff --git a/cmd/aws-sso-creds/export/cli.go b/cmd/aws-sso-creds/export/cli.go index 7e6a400..90bbafb 100644 --- a/cmd/aws-sso-creds/export/cli.go +++ b/cmd/aws-sso-creds/export/cli.go @@ -10,9 +10,9 @@ import ( func Command() *cobra.Command { command := &cobra.Command{ - Use: "export", - Short: "Generates a set of shell commands to export AWS temporary creds to your environment", - Long: "Generates a set of shell commands to export AWS temporary creds to your environment", + Use: "export", + Short: "Generates a set of shell commands to export AWS temporary creds to your environment", + Long: "Generates a set of shell commands to export AWS temporary creds to your environment", SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { diff --git a/cmd/aws-sso-creds/get/cli.go b/cmd/aws-sso-creds/get/cli.go index 50f509a..c95ec30 100644 --- a/cmd/aws-sso-creds/get/cli.go +++ b/cmd/aws-sso-creds/get/cli.go @@ -9,15 +9,14 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" - . "github.com/logrusorgru/aurora" + "github.com/logrusorgru/aurora" ) - func Command() *cobra.Command { command := &cobra.Command{ - Use: "get", - Short: "Get AWS temporary credentials to use on the command line", - Long: "Retrieve AWS temporary credentials", + Use: "get", + Short: "Get AWS temporary credentials to use on the command line", + Long: "Retrieve AWS temporary credentials", SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -36,7 +35,7 @@ func Command() *cobra.Command { return err } - fmt.Println(Sprintf("Your temporary credentials for account %s are:", White(accountID))) + fmt.Println(aurora.Sprintf("Your temporary credentials for account %s are:", aurora.White(accountID))) fmt.Println("") fmt.Fprintln(os.Stdout, "AWS_ACCESS_KEY_ID\t", *creds.RoleCredentials.AccessKeyId) @@ -45,7 +44,7 @@ func Command() *cobra.Command { fmt.Println("") - fmt.Println("These credentials will expire at:", Red(time.UnixMilli(*creds.RoleCredentials.Expiration).UTC())) + fmt.Println("These credentials will expire at:", aurora.Red(time.UnixMilli(*creds.RoleCredentials.Expiration).UTC())) return nil }, diff --git a/cmd/aws-sso-creds/helper/cli.go b/cmd/aws-sso-creds/helper/cli.go index 5cb7481..cdf05f2 100644 --- a/cmd/aws-sso-creds/helper/cli.go +++ b/cmd/aws-sso-creds/helper/cli.go @@ -11,7 +11,7 @@ import ( type CredentialsProcessOutput struct { Version int `json:"page"` - AccessKeyId string `json:"AccessKeyId"` + AccessKeyID string `json:"AccessKeyId"` SecretAccessKey string `json:"SecretAccessKey"` SessionToken string `json:"SessionToken"` Expiration string `json:"Expiration"` @@ -41,11 +41,11 @@ func Command() *cobra.Command { } rawCreds := CredentialsProcessOutput{ - Version: 1, - AccessKeyId: *creds.RoleCredentials.AccessKeyId, + Version: 1, + AccessKeyID: *creds.RoleCredentials.AccessKeyId, SecretAccessKey: *creds.RoleCredentials.SecretAccessKey, - SessionToken: *creds.RoleCredentials.SessionToken, - Expiration: time.Unix(*creds.RoleCredentials.Expiration / 1000, 0).Format(time.RFC3339), + SessionToken: *creds.RoleCredentials.SessionToken, + Expiration: time.Unix(*creds.RoleCredentials.Expiration/1000, 0).Format(time.RFC3339), } output, err := json.Marshal(rawCreds) diff --git a/cmd/aws-sso-creds/list/accounts/cli.go b/cmd/aws-sso-creds/list/accounts/cli.go index ab90355..0e8116d 100644 --- a/cmd/aws-sso-creds/list/accounts/cli.go +++ b/cmd/aws-sso-creds/list/accounts/cli.go @@ -2,8 +2,9 @@ package accounts import ( "fmt" - "io/ioutil" + "io/fs" "os" + "path/filepath" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" @@ -47,12 +48,17 @@ func Command() *cobra.Command { return fmt.Errorf("error retrieving SSO config: %w", err) } - cacheFiles, err := ioutil.ReadDir(fmt.Sprintf("%s/.aws/sso/cache", homeDir)) + cacheFiles, err := os.ReadDir(filepath.Join(homeDir, ".aws", "sso", "cache")) if err != nil { return fmt.Errorf("error retrieving cache files - perhaps you need to login?: %w", err) } - token, err := config.GetSSOToken(cacheFiles, *ssoConfig, homeDir) + files := make([]fs.FileInfo, 0, len(cacheFiles)) + + token, err := config.GetSSOToken(files, *ssoConfig, homeDir) + if err != nil { + return fmt.Errorf("error retrieving SSO token from cache files: %v", err) + } sess := session.Must(session.NewSession()) svc := sso.New(sess, aws.NewConfig().WithRegion(ssoConfig.Region)) @@ -61,6 +67,9 @@ func Command() *cobra.Command { AccessToken: &token, MaxResults: &results, }) + if err != nil { + return fmt.Errorf("error listing accounts: %v", err) + } writer := tabwriter.NewWriter(os.Stdout, tabwriterMinWidth, tabwriterWidth, tabwriterPadding, tabwriterPadChar, tabwriterFlags) fmt.Fprintln(writer, "ID\tNAME\tEMAIL ADDRESS") diff --git a/cmd/aws-sso-creds/list/roles/cli.go b/cmd/aws-sso-creds/list/roles/cli.go index 0de5d10..6a10451 100644 --- a/cmd/aws-sso-creds/list/roles/cli.go +++ b/cmd/aws-sso-creds/list/roles/cli.go @@ -2,6 +2,10 @@ package roles import ( "fmt" + "io/fs" + "os" + "path/filepath" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/sso" @@ -9,8 +13,6 @@ import ( "github.com/liggitt/tabwriter" "github.com/spf13/cobra" "github.com/spf13/viper" - "io/ioutil" - "os" ) const ( @@ -22,7 +24,7 @@ const ( ) var ( - results int64 + results int64 accountID string ) @@ -48,12 +50,17 @@ func Command() *cobra.Command { return fmt.Errorf("error retrieving SSO config: %w", err) } - cacheFiles, err := ioutil.ReadDir(fmt.Sprintf("%s/.aws/sso/cache", homeDir)) + cacheFiles, err := os.ReadDir(filepath.Join(homeDir, ".aws", "sso", "cache")) if err != nil { return fmt.Errorf("error retrieving cache files - perhaps you need to login?: %w", err) } - token, err := config.GetSSOToken(cacheFiles, *ssoConfig, homeDir) + files := make([]fs.FileInfo, 0, len(cacheFiles)) + + token, err := config.GetSSOToken(files, *ssoConfig, homeDir) + if err != nil { + return fmt.Errorf("error retrieving SSO token from cache files: %v", err) + } sess := session.Must(session.NewSession()) svc := sso.New(sess, aws.NewConfig().WithRegion(ssoConfig.Region)) @@ -62,9 +69,12 @@ func Command() *cobra.Command { roles, err := svc.ListAccountRoles(&sso.ListAccountRolesInput{ AccessToken: &token, - MaxResults: &results, - AccountId: &accountID, + MaxResults: &results, + AccountId: &accountID, }) + if err != nil { + return fmt.Errorf("error listing roles: %v", err) + } writer := tabwriter.NewWriter(os.Stdout, tabwriterMinWidth, tabwriterWidth, tabwriterPadding, tabwriterPadChar, tabwriterFlags) fmt.Fprintln(writer, "ID\tROLE NAME") diff --git a/cmd/aws-sso-creds/main.go b/cmd/aws-sso-creds/main.go index 1d1522f..b2dfb0d 100644 --- a/cmd/aws-sso-creds/main.go +++ b/cmd/aws-sso-creds/main.go @@ -40,9 +40,15 @@ func configureCLI() *cobra.Command { rootCommand.PersistentFlags().StringVarP(&profile, "profile", "p", "", "the AWS profile to use") rootCommand.PersistentFlags().StringVarP(&homeDir, "home-directory", "H", homeDir, "specify a path to a home directory") - viper.BindEnv("profile", "AWS_PROFILE") - viper.BindPFlag("profile", rootCommand.PersistentFlags().Lookup("profile")) - viper.BindPFlag("home-directory", rootCommand.PersistentFlags().Lookup("home-directory")) + if err := viper.BindEnv("profile", "AWS_PROFILE"); err != nil { + panic(err) + } + if err := viper.BindPFlag("profile", rootCommand.PersistentFlags().Lookup("profile")); err != nil { + panic(err) + } + if err := viper.BindPFlag("home-directory", rootCommand.PersistentFlags().Lookup("home-directory")); err != nil { + panic(err) + } return rootCommand } diff --git a/cmd/aws-sso-creds/set/cli.go b/cmd/aws-sso-creds/set/cli.go index 77879c8..f7fbbfb 100644 --- a/cmd/aws-sso-creds/set/cli.go +++ b/cmd/aws-sso-creds/set/cli.go @@ -3,8 +3,8 @@ package set import ( "fmt" "os" - "time" "path/filepath" + "time" "github.com/jaxxstorm/aws-sso-creds/pkg/credentials" "github.com/spf13/cobra" @@ -13,6 +13,10 @@ import ( "github.com/bigkevmcd/go-configparser" ) +var ( + credsFile *configparser.ConfigParser +) + func Command() *cobra.Command { command := &cobra.Command{ Use: "set PROFILE", @@ -24,7 +28,7 @@ func Command() *cobra.Command { cmd.SilenceUsage = true profile := viper.GetString("profile") homeDir := viper.GetString("home-directory") - + credsPath := filepath.Join(homeDir, ".aws", "credentials") cfgPath := filepath.Join(homeDir, ".aws", "config") @@ -40,17 +44,18 @@ func Command() *cobra.Command { return err } - credsFile, err := configparser.NewConfigParserFromFile(credsPath) - if os.IsNotExist(err) { - // Ensure the new empty credentials file is not readable by others. - if f, err := os.OpenFile(credsPath, os.O_CREATE, 0600); err != nil { - return err - } else { - f.Close() + credsFile, err = configparser.NewConfigParserFromFile(credsPath) + if err != nil { + if os.IsNotExist(err) { + // Ensure the new empty credentials file is not readable by others. + if f, err := os.OpenFile(credsPath, os.O_CREATE, 0600); err != nil { + f.Close() + return err + } + + credsFile = configparser.New() } - credsFile = configparser.New() - } else if err != nil { - return err + return fmt.Errorf("error parsing config file: %v", err) } configFile, err := configparser.NewConfigParserFromFile(cfgPath) @@ -59,15 +64,31 @@ func Command() *cobra.Command { } // create a new credentials section - credsFile.AddSection(args[0]) - configFile.AddSection(fmt.Sprintf("profile %s", args[0])) + if err := credsFile.AddSection(args[0]); err != nil { + return fmt.Errorf("error creating credentials section in creds file: %v", err) + } - credsFile.Set(args[0], "aws_access_key_id", *creds.RoleCredentials.AccessKeyId) - credsFile.Set(args[0], "aws_secret_access_key", *creds.RoleCredentials.SecretAccessKey) - credsFile.Set(args[0], "aws_session_token", *creds.RoleCredentials.SessionToken) + if err := configFile.AddSection(fmt.Sprintf("profile %s", args[0])); err != nil { + return fmt.Errorf("error creating credentials section in config file: %v", err) + } + + if err := credsFile.Set(args[0], "aws_access_key_id", *creds.RoleCredentials.AccessKeyId); err != nil { + return fmt.Errorf("error setting access key id: %v", err) + } + if err := credsFile.Set(args[0], "aws_secret_access_key", *creds.RoleCredentials.SecretAccessKey); err != nil { + return fmt.Errorf("error setting secret access key: %v", err) + } + if err := credsFile.Set(args[0], "aws_session_token", *creds.RoleCredentials.SessionToken); err != nil { + return fmt.Errorf("error setting session token: %v", err) + } + + if err := credsFile.SaveWithDelimiter(credsPath, "="); err != nil { + return fmt.Errorf("error saving credentials file: %v", err) + } - credsFile.SaveWithDelimiter(credsPath, "=") - configFile.SaveWithDelimiter(cfgPath, "=") + if err := configFile.SaveWithDelimiter(cfgPath, "="); err != nil { + return fmt.Errorf("error saving config file: %v", err) + } fmt.Printf("credentials saved to profile: %s\n", args[0]) fmt.Printf("these credentials will expire: %s\n", time.Unix(*creds.RoleCredentials.Expiration, 0).Format(time.UnixDate)) diff --git a/cmd/aws-sso-creds/version/cli.go b/cmd/aws-sso-creds/version/cli.go index 159f326..a24dfee 100644 --- a/cmd/aws-sso-creds/version/cli.go +++ b/cmd/aws-sso-creds/version/cli.go @@ -5,8 +5,8 @@ import ( "os" "github.com/go-git/go-git/v5/plumbing" - "github.com/pulumi/pulumictl/pkg/gitversion" "github.com/jaxxstorm/aws-sso-creds/pkg/version" + "github.com/pulumi/pulumictl/pkg/gitversion" "github.com/spf13/cobra" ) diff --git a/go.mod b/go.mod index 2dd9220..b9851ef 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/jaxxstorm/aws-sso-creds -go 1.17 +go 1.19 require ( github.com/aws/aws-sdk-go v1.43.15 diff --git a/pkg/config/config.go b/pkg/config/config.go index 261a9b9..4066d39 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -22,15 +22,15 @@ func GetSSOConfig(profile string, homedir string) (*SSOConfig, error) { // FIXME: make this better if p.HasSection(section) { - ssoStartUrl, err := p.Get(section, "sso_start_url") + ssoStartURL, err := p.Get(section, "sso_start_url") if err != nil { return nil, fmt.Errorf("no SSO url in profile: %s", profile) } ssoRegion, err := p.Get(section, "sso_region") if err != nil { - return nil, fmt.Errorf("no SSO region in profile: %s", profile) + return nil, fmt.Errorf("no SSO region in profile: %s", profile) } - ssoAccountId, err := p.Get(section, "sso_account_id") + ssoAccountID, err := p.Get(section, "sso_account_id") if err != nil { return nil, fmt.Errorf("no SSO account id in profile: %s", profile) } @@ -40,13 +40,14 @@ func GetSSOConfig(profile string, homedir string) (*SSOConfig, error) { } return &SSOConfig{ - StartUrl: ssoStartUrl, + StartURL: ssoStartURL, Region: ssoRegion, - AccountID: ssoAccountId, + AccountID: ssoAccountID, RoleName: ssoRoleName, }, nil - } else { - return nil, fmt.Errorf("unable to find profile %s", profile) } + + return nil, fmt.Errorf("unable to find profile %s", profile) + } diff --git a/pkg/config/token.go b/pkg/config/token.go index 487a9e2..c82e2ed 100644 --- a/pkg/config/token.go +++ b/pkg/config/token.go @@ -3,8 +3,8 @@ package config import ( "encoding/json" "fmt" - "io/ioutil" "os" + "path/filepath" "strings" "time" ) @@ -16,18 +16,20 @@ func GetSSOToken(files []os.FileInfo, ssoConfig SSOConfig, homedir string) (stri // loop through all the files for _, file := range files { // read the contents into a JSON byte - jsonContent, err := ioutil.ReadFile(fmt.Sprintf("%s/.aws/sso/cache/%s", homedir, file.Name())) + jsonContent, err := os.ReadFile(filepath.Join(homedir, ".aws", "sso", "cache", file.Name())) if err != nil { - panic(err) + return "", fmt.Errorf("error reading aws SSO cache file: %v", err) } // initialize some SSOCacheConfig var cacheData SSOCacheConfig - json.Unmarshal(jsonContent, &cacheData) + if err := json.Unmarshal(jsonContent, &cacheData); err != nil { + return "", fmt.Errorf("error marshalling JSON data from cache file: %v", err) + } // check if the file has a start url, if it doesn't, ignore it - if cacheData.StartUrl == ssoConfig.StartUrl { + if cacheData.StartURL == ssoConfig.StartURL { // check if the file has an expiry time, if it doesn't ignore it if cacheData.ExpiresAt != "" { t, err := time.Parse(time.RFC3339, strings.Replace(cacheData.ExpiresAt, "UTC", "+00:00", -1)) diff --git a/pkg/config/types.go b/pkg/config/types.go index 40a8721..82abdce 100644 --- a/pkg/config/types.go +++ b/pkg/config/types.go @@ -1,14 +1,14 @@ package config type SSOConfig struct { - StartUrl string + StartURL string Region string AccountID string RoleName string } type SSOCacheConfig struct { - StartUrl string `json:"startUrl"` + StartURL string `json:"startUrl"` Region string `json:"region"` AccessToken string `json:"accessToken"` ExpiresAt string `json:"expiresAt"` diff --git a/pkg/credentials/creds.go b/pkg/credentials/creds.go index b863816..a647840 100644 --- a/pkg/credentials/creds.go +++ b/pkg/credentials/creds.go @@ -2,7 +2,9 @@ package credentials import ( "fmt" - "io/ioutil" + "io/fs" + "os" + "path/filepath" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" @@ -17,12 +19,14 @@ func GetSSOCredentials(profile string, homedir string) (*sso.GetRoleCredentialsO return nil, "", fmt.Errorf("error retrieving SSO config: %w", err) } - cacheFiles, err := ioutil.ReadDir(fmt.Sprintf("%s/.aws/sso/cache", homedir)) + cacheFiles, err := os.ReadDir(filepath.Join(homedir, ".aws", "sso", "cache")) if err != nil { return nil, "", fmt.Errorf("error retrieving cache files - perhaps you need to login?: %w", err) } - token, err := config.GetSSOToken(cacheFiles, *ssoConfig, homedir) + files := make([]fs.FileInfo, 0, len(cacheFiles)) + + token, err := config.GetSSOToken(files, *ssoConfig, homedir) if err != nil { return nil, "", fmt.Errorf("error retrieving SSO token from cache files: %w", err) }