From 8912fd2eaf056fc689d554372a534a1cd3d89332 Mon Sep 17 00:00:00 2001 From: Patrick Sanders Date: Fri, 27 Aug 2021 10:34:33 -0700 Subject: [PATCH] feat: Add whoami command (#85) --- cmd/credential_process.go | 4 +- cmd/export.go | 4 +- cmd/file.go | 4 +- cmd/vars.go | 5 +++ cmd/whoami.go | 59 +++++++++++++++++++++++++ configs/example-config.yaml | 4 ++ internal/{creds => aws}/aws.go | 55 +++++++++++++---------- internal/aws/types.go | 11 +++++ internal/cache/cache_test.go | 19 ++++---- internal/config/config.go | 3 ++ internal/creds/consoleme.go | 48 ++++++++++++++++---- internal/creds/refreshable.go | 7 ++- internal/creds/refreshable_test.go | 21 +++++---- internal/creds/types.go | 71 +++--------------------------- internal/swag/swag.go | 49 +++++++++++++++++++++ internal/types/types.go | 55 +++++++++++++++++++++++ 16 files changed, 301 insertions(+), 118 deletions(-) create mode 100644 cmd/whoami.go rename internal/{creds => aws}/aws.go (64%) create mode 100644 internal/aws/types.go create mode 100644 internal/swag/swag.go create mode 100644 internal/types/types.go diff --git a/cmd/credential_process.go b/cmd/credential_process.go index 4606c7d..f3f6515 100644 --- a/cmd/credential_process.go +++ b/cmd/credential_process.go @@ -21,6 +21,8 @@ import ( "fmt" "time" + "github.com/netflix/weep/internal/aws" + "github.com/netflix/weep/internal/creds" "github.com/netflix/weep/internal/util" @@ -114,7 +116,7 @@ func runCredentialProcess(cmd *cobra.Command, args []string) error { return nil } -func printCredentialProcess(credentials *creds.AwsCredentials) { +func printCredentialProcess(credentials *aws.Credentials) { expirationTimeFormat := credentials.Expiration.Format(time.RFC3339) credentialProcessOutput := &creds.CredentialProcess{ diff --git a/cmd/export.go b/cmd/export.go index 0af491a..154717d 100644 --- a/cmd/export.go +++ b/cmd/export.go @@ -21,6 +21,8 @@ import ( "os" "strings" + "github.com/netflix/weep/internal/aws" + "github.com/netflix/weep/internal/creds" "github.com/spf13/cobra" @@ -60,7 +62,7 @@ func isFish() bool { } } -func printExport(creds *creds.AwsCredentials) { +func printExport(creds *aws.Credentials) { if isFish() { // fish has a different way of setting variables than bash/zsh and others fmt.Printf("set -x AWS_ACCESS_KEY_ID %s && set -x AWS_SECRET_ACCESS_KEY %s && set -x AWS_SESSION_TOKEN %s\n", diff --git a/cmd/file.go b/cmd/file.go index 9663f3f..adb5a31 100644 --- a/cmd/file.go +++ b/cmd/file.go @@ -22,6 +22,8 @@ import ( "strconv" "time" + "github.com/netflix/weep/internal/aws" + "github.com/netflix/weep/internal/creds" "github.com/netflix/weep/internal/util" @@ -153,7 +155,7 @@ func isExpiring(filename, profile string, thresholdMinutes int) (bool, error) { return false, nil } -func writeCredentialsFile(credentials *creds.AwsCredentials, profile, filename string) error { +func writeCredentialsFile(credentials *aws.Credentials, profile, filename string) error { var credentialsINI *ini.File var err error diff --git a/cmd/vars.go b/cmd/vars.go index 37761fd..7345c51 100644 --- a/cmd/vars.go +++ b/cmd/vars.go @@ -123,3 +123,8 @@ system. var versionShortHelp = "Print version information" var versionLongHelp = `` + +var whoamiShortHelp = "Print information about current AWS credentials" +var whoamiLongHelp = `The whoami command retrieves information about your AWS credentials from AWS STS using the default +credential provider chain. If SWAG (https://github.com/Netflix-Skunkworks/swag-api) is enabled, weep will +attempt to enrich the output with additional data.` diff --git a/cmd/whoami.go b/cmd/whoami.go new file mode 100644 index 0000000..fa39b26 --- /dev/null +++ b/cmd/whoami.go @@ -0,0 +1,59 @@ +package cmd + +import ( + "fmt" + "os" + "strings" + "text/tabwriter" + + "github.com/netflix/weep/internal/aws" + "github.com/netflix/weep/internal/swag" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +func init() { + rootCmd.AddCommand(whoamiCmd) +} + +var whoamiCmd = &cobra.Command{ + Use: "whoami", + Short: whoamiShortHelp, + Long: whoamiLongHelp, + RunE: runWhoami, + SilenceUsage: true, +} + +func runWhoami(cmd *cobra.Command, args []string) error { + session := aws.GetSession() + callerIdentity, err := aws.GetCallerIdentity(session) + if err != nil { + return err + } + var name string + if viper.GetBool("swag.enable") { + name, err = swag.AccountName(*callerIdentity.Account) + if err != nil { + cmd.Printf("Failed to get account info from SWAG: %v\n", err) + } + } + role := roleFromArn(*callerIdentity.Arn) + + w := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', 0) + fmt.Fprintf(w, "Role:\t%s\n", role) + if name != "" { + fmt.Fprintf(w, "Account:\t%s (%s)\n", name, *callerIdentity.Account) + } else { + fmt.Fprintf(w, "Account:\t%s\n", *callerIdentity.Account) + } + fmt.Fprintf(w, "ARN:\t%s\n", *callerIdentity.Arn) + fmt.Fprintf(w, "UserId:\t%s\n", *callerIdentity.UserId) + w.Flush() + + return nil +} + +func roleFromArn(arn string) string { + parts := strings.Split(arn, "/") + return parts[1] +} diff --git a/configs/example-config.yaml b/configs/example-config.yaml index 975b1e2..db5b8ec 100644 --- a/configs/example-config.yaml +++ b/configs/example-config.yaml @@ -17,6 +17,10 @@ service: - debug args: # Args are command arguments. This configuration will start the metadata service with credentials for roleName - roleName +swag: # Optionally use SWAG (https://github.com/Netflix-Skunkworks/swag-api) for AWS account information + enabled: false + use_mtls: false + url: https://swag.example.com/api #challenge_settings: # (Optional) Username can be provided. If it is not provided, user will be prompted on first authentication attempt # user: you@example.com mtls_settings: # only needed if authentication_method is mtls diff --git a/internal/creds/aws.go b/internal/aws/aws.go similarity index 64% rename from internal/creds/aws.go rename to internal/aws/aws.go index c117462..147dedb 100644 --- a/internal/creds/aws.go +++ b/internal/aws/aws.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package creds +package aws import ( "fmt" @@ -25,9 +25,13 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/iam" "github.com/aws/aws-sdk-go/service/sts" + "github.com/netflix/weep/internal/logging" ) +var log = logging.GetLogger() + // getSessionName returns the AWS session name, or defaults to weep if we can't find one. func getSessionName(session *sts.STS) string { identity, err := session.GetCallerIdentity(&sts.GetCallerIdentityInput{}) @@ -46,8 +50,8 @@ func getSessionName(session *sts.STS) string { return splitId[1] } -// getAssumeRoleCredentials uses the provided credentials to assume the role specified by roleArn. -func getAssumeRoleCredentials(id, secret, token, roleArn string) (string, string, string, error) { +// GetAssumeRoleCredentials uses the provided credentials to assume the role specified by roleArn. +func GetAssumeRoleCredentials(id, secret, token, roleArn string) (string, string, string, error) { region := viper.GetString("aws.region") staticCreds := credentials.NewStaticCredentials(id, secret, token) awsSession := session.Must(session.NewSessionWithOptions(session.Options{ @@ -73,32 +77,35 @@ func getAssumeRoleCredentials(id, secret, token, roleArn string) (string, string return *stsCreds.Credentials.AccessKeyId, *stsCreds.Credentials.SecretAccessKey, *stsCreds.Credentials.SessionToken, nil } -// GetCredentialsC uses the provided Client to request credentials from ConsoleMe then -// follows the provided chain of roles to assume. Roles are assumed in the order in which -// they appear in the assumeRole slice. -func GetCredentialsC(client HTTPClient, role string, ipRestrict bool, assumeRole []string) (*AwsCredentials, error) { - resp, err := client.GetRoleCredentials(role, ipRestrict) - if err != nil { - return nil, err - } +func GetSession() *session.Session { + return session.Must(session.NewSession()) +} - for _, assumeRoleArn := range assumeRole { - resp.AccessKeyId, resp.SecretAccessKey, resp.SessionToken, err = getAssumeRoleCredentials(resp.AccessKeyId, resp.SecretAccessKey, resp.SessionToken, assumeRoleArn) - if err != nil { - return nil, fmt.Errorf("role assumption failed for %s: %s", assumeRoleArn, err) - } +func GetCallerIdentity(awsSession *session.Session) (*sts.GetCallerIdentityOutput, error) { + if awsSession == nil { + awsSession = GetSession() } - - return resp, nil + stsSession := sts.New(awsSession) + input := &sts.GetCallerIdentityInput{} + return stsSession.GetCallerIdentity(input) } -// GetCredentials requests credentials from ConsoleMe then follows the provided chain of roles to -// assume. Roles are assumed in the order in which they appear in the assumeRole slice. -func GetCredentials(role string, ipRestrict bool, assumeRole []string, region string) (*AwsCredentials, error) { - client, err := GetClient(region) +func ListAccountAliases(awsSession *session.Session) ([]*string, error) { + aliases := make([]*string, 0) + pageNum := 0 + if awsSession == nil { + awsSession = GetSession() + } + iamSession := iam.New(awsSession) + input := &iam.ListAccountAliasesInput{} + err := iamSession.ListAccountAliasesPages(input, func(page *iam.ListAccountAliasesOutput, lastPage bool) bool { + pageNum++ + fmt.Println(page) + aliases = append(aliases, page.AccountAliases...) + return !*page.IsTruncated + }) if err != nil { return nil, err } - - return GetCredentialsC(client, role, ipRestrict, assumeRole) + return aliases, nil } diff --git a/internal/aws/types.go b/internal/aws/types.go new file mode 100644 index 0000000..eefcc08 --- /dev/null +++ b/internal/aws/types.go @@ -0,0 +1,11 @@ +package aws + +import "github.com/netflix/weep/internal/types" + +type Credentials struct { + AccessKeyId string `json:"AccessKeyId"` + SecretAccessKey string `json:"SecretAccessKey"` + SessionToken string `json:"SessionToken"` + Expiration types.Time `json:"Expiration"` + RoleArn string `json:"RoleArn"` +} diff --git a/internal/cache/cache_test.go b/internal/cache/cache_test.go index ab0d2d0..24e1e76 100644 --- a/internal/cache/cache_test.go +++ b/internal/cache/cache_test.go @@ -20,6 +20,9 @@ import ( "testing" "time" + "github.com/netflix/weep/internal/aws" + "github.com/netflix/weep/internal/types" + "github.com/netflix/weep/internal/creds" "github.com/netflix/weep/internal/errors" ) @@ -189,11 +192,11 @@ func TestCredentialCache_SetDefault(t *testing.T) { expectedRole := "a" expectedExpiration := time.Unix(1, 0).Round(0) testClient, err := creds.GetTestClient(creds.ConsolemeCredentialResponseType{ - Credentials: &creds.AwsCredentials{ + Credentials: &aws.Credentials{ AccessKeyId: "a", SecretAccessKey: "b", SessionToken: "c", - Expiration: creds.Time(time.Unix(1, 0)), + Expiration: types.Time(time.Unix(1, 0)), RoleArn: "e", }, }) @@ -217,11 +220,11 @@ func TestCredentialCache_DefaultLastUpdated(t *testing.T) { RoleCredentials: map[string]*creds.RefreshableProvider{}, } testClient, err := creds.GetTestClient(creds.ConsolemeCredentialResponseType{ - Credentials: &creds.AwsCredentials{ + Credentials: &aws.Credentials{ AccessKeyId: "a", SecretAccessKey: "b", SessionToken: "c", - Expiration: creds.Time(time.Unix(1, 0)), + Expiration: types.Time(time.Unix(1, 0)), RoleArn: "e", }, }) @@ -260,11 +263,11 @@ func TestCredentialCache_DefaultArn(t *testing.T) { RoleCredentials: map[string]*creds.RefreshableProvider{}, } testClient, err := creds.GetTestClient(creds.ConsolemeCredentialResponseType{ - Credentials: &creds.AwsCredentials{ + Credentials: &aws.Credentials{ AccessKeyId: "a", SecretAccessKey: "b", SessionToken: "c", - Expiration: creds.Time(time.Unix(1, 0)), + Expiration: types.Time(time.Unix(1, 0)), RoleArn: "e", }, }) @@ -339,11 +342,11 @@ func TestCredentialCache_GetOrSet(t *testing.T) { RoleCredentials: tc.CacheContents, } client, err := creds.GetTestClient(creds.ConsolemeCredentialResponseType{ - Credentials: &creds.AwsCredentials{ + Credentials: &aws.Credentials{ AccessKeyId: "a", SecretAccessKey: "b", SessionToken: "c", - Expiration: creds.Time(time.Unix(1, 0)), + Expiration: types.Time(time.Unix(1, 0)), RoleArn: tc.ExpectedResult.RoleArn, }, }) diff --git a/internal/config/config.go b/internal/config/config.go index d8b8054..a4c9cf1 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -47,6 +47,9 @@ func init() { viper.SetDefault("service.run", []string{"service", "run"}) viper.SetDefault("service.args", []string{}) viper.SetDefault("service.flags", []string{}) + viper.SetDefault("swag.enable", false) + viper.SetDefault("swag.use_mtls", false) + viper.SetDefault("swag.url", "") // Set aliases for backward-compatibility viper.RegisterAlias("server.ecs_credential_provider_port", "server.port") diff --git a/internal/creds/consoleme.go b/internal/creds/consoleme.go index 5f7f9a6..f60225f 100644 --- a/internal/creds/consoleme.go +++ b/internal/creds/consoleme.go @@ -30,6 +30,8 @@ import ( "strings" "time" + "github.com/netflix/weep/internal/aws" + werrors "github.com/netflix/weep/internal/errors" "github.com/netflix/weep/internal/httpAuth/challenge" "github.com/netflix/weep/internal/httpAuth/mtls" @@ -52,7 +54,7 @@ type Account struct { // HTTPClient is the interface we expect HTTP clients to implement. type HTTPClient interface { Do(req *http.Request) (*http.Response, error) - GetRoleCredentials(role string, ipRestrict bool) (*AwsCredentials, error) + GetRoleCredentials(role string, ipRestrict bool) (*aws.Credentials, error) CloseIdleConnections() buildRequest(string, string, io.Reader, string) (*http.Request, error) } @@ -258,11 +260,11 @@ func parseError(statusCode int, rawErrorResponse []byte) error { } } -func (c *Client) GetRoleCredentials(role string, ipRestrict bool) (*AwsCredentials, error) { +func (c *Client) GetRoleCredentials(role string, ipRestrict bool) (*aws.Credentials, error) { return getRoleCredentialsFunc(c, role, ipRestrict) } -func getRoleCredentialsFunc(c HTTPClient, role string, ipRestrict bool) (*AwsCredentials, error) { +func getRoleCredentialsFunc(c HTTPClient, role string, ipRestrict bool) (*aws.Credentials, error) { var credentialsResponse ConsolemeCredentialResponseType cmCredRequest := ConsolemeCredentialRequestType{ @@ -328,10 +330,10 @@ func defaultTransport() *http.Transport { type ClientMock struct { DoFunc func(req *http.Request) (*http.Response, error) - GetRoleCredentialsFunc func(role string, ipRestrict bool) (*AwsCredentials, error) + GetRoleCredentialsFunc func(role string, ipRestrict bool) (*aws.Credentials, error) } -func (c *ClientMock) GetRoleCredentials(role string, ipRestrict bool) (*AwsCredentials, error) { +func (c *ClientMock) GetRoleCredentials(role string, ipRestrict bool) (*aws.Credentials, error) { return getRoleCredentialsFunc(c, role, ipRestrict) } @@ -346,7 +348,7 @@ func (c *ClientMock) Do(req *http.Request) (*http.Response, error) { } func GetTestClient(responseBody interface{}) (HTTPClient, error) { - var responseCredentials *AwsCredentials + var responseCredentials *aws.Credentials var responseCode = 200 if c, ok := responseBody.(ConsolemeCredentialResponseType); ok { responseCredentials = c.Credentials @@ -370,12 +372,42 @@ func GetTestClient(responseBody interface{}) (HTTPClient, error) { Body: r, }, nil }, - GetRoleCredentialsFunc: func(role string, ipRestrict bool) (*AwsCredentials, error) { + GetRoleCredentialsFunc: func(role string, ipRestrict bool) (*aws.Credentials, error) { if responseCredentials != nil { return responseCredentials, nil } - return &AwsCredentials{RoleArn: role}, nil + return &aws.Credentials{RoleArn: role}, nil }, } return client, nil } + +// GetCredentialsC uses the provided Client to request credentials from ConsoleMe then +// follows the provided chain of roles to assume. Roles are assumed in the order in which +// they appear in the assumeRole slice. +func GetCredentialsC(client HTTPClient, role string, ipRestrict bool, assumeRole []string) (*aws.Credentials, error) { + resp, err := client.GetRoleCredentials(role, ipRestrict) + if err != nil { + return nil, err + } + + for _, assumeRoleArn := range assumeRole { + resp.AccessKeyId, resp.SecretAccessKey, resp.SessionToken, err = aws.GetAssumeRoleCredentials(resp.AccessKeyId, resp.SecretAccessKey, resp.SessionToken, assumeRoleArn) + if err != nil { + return nil, fmt.Errorf("role assumption failed for %s: %s", assumeRoleArn, err) + } + } + + return resp, nil +} + +// GetCredentials requests credentials from ConsoleMe then follows the provided chain of roles to +// assume. Roles are assumed in the order in which they appear in the assumeRole slice. +func GetCredentials(role string, ipRestrict bool, assumeRole []string, region string) (*aws.Credentials, error) { + client, err := GetClient(region) + if err != nil { + return nil, err + } + + return GetCredentialsC(client, role, ipRestrict, assumeRole) +} diff --git a/internal/creds/refreshable.go b/internal/creds/refreshable.go index 671596b..de86198 100644 --- a/internal/creds/refreshable.go +++ b/internal/creds/refreshable.go @@ -21,6 +21,9 @@ import ( "strings" "time" + "github.com/netflix/weep/internal/aws" + "github.com/netflix/weep/internal/types" + "github.com/netflix/weep/internal/errors" "github.com/sirupsen/logrus" @@ -84,7 +87,7 @@ func (rp *RefreshableProvider) checkAndRefresh(threshold int) (bool, error) { func (rp *RefreshableProvider) refresh() error { log.Debugf("refreshing credentials for %s", rp.RoleArn) var err error - var newCreds *AwsCredentials + var newCreds *aws.Credentials rp.Lock() defer rp.Unlock() @@ -113,7 +116,7 @@ func (rp *RefreshableProvider) refresh() error { rp.value.SessionToken = newCreds.SessionToken rp.value.SecretAccessKey = newCreds.SecretAccessKey rp.value.AccessKeyID = newCreds.AccessKeyId - rp.LastRefreshed = Time(time.Now()) + rp.LastRefreshed = types.Time(time.Now()) if newCreds.RoleArn != "" { // We favor the role ARN from ConsoleMe over the one from the user, which could just be a search string. rp.RoleArn = newCreds.RoleArn diff --git a/internal/creds/refreshable_test.go b/internal/creds/refreshable_test.go index 7295a27..9134763 100644 --- a/internal/creds/refreshable_test.go +++ b/internal/creds/refreshable_test.go @@ -20,6 +20,9 @@ import ( "testing" "time" + "github.com/netflix/weep/internal/aws" + "github.com/netflix/weep/internal/types" + "github.com/netflix/weep/internal/errors" "github.com/aws/aws-sdk-go/aws/credentials" @@ -33,10 +36,10 @@ var ( testRole = "e" testRoleArn = "f" testProviderName = "g" - testExpiration = Time(time.Now().Add(60 * time.Minute).Round(0)) - testSoonExpiration = Time(time.Now().Add(5 * time.Minute).Round(0)) - testPastExpiration = Time(time.Now().Add(-5 * time.Minute).Round(0)) - testCredentials = &AwsCredentials{ + testExpiration = types.Time(time.Now().Add(60 * time.Minute).Round(0)) + testSoonExpiration = types.Time(time.Now().Add(5 * time.Minute).Round(0)) + testPastExpiration = types.Time(time.Now().Add(-5 * time.Minute).Round(0)) + testCredentials = &aws.Credentials{ AccessKeyId: testAccessKeyId, SecretAccessKey: testSecretAccessKey, SessionToken: testSessionToken, @@ -69,7 +72,7 @@ func TestNewRefreshableProvider(t *testing.T) { ExpectedError: nil, ExpectedResult: &RefreshableProvider{ Expiration: testExpiration, - LastRefreshed: Time{}, + LastRefreshed: types.Time{}, Region: testRegion, RoleName: testRole, RoleArn: testRoleArn, @@ -87,7 +90,7 @@ func TestNewRefreshableProvider(t *testing.T) { ExpectedError: nil, ExpectedResult: &RefreshableProvider{ Expiration: testExpiration, - LastRefreshed: Time{}, + LastRefreshed: types.Time{}, Region: testRegion, RoleName: testRole, RoleArn: testRoleArn, @@ -181,7 +184,7 @@ func TestRefreshableProvider_refresh(t *testing.T) { }, } - zeroTime := Time{} + zeroTime := types.Time{} for i, tc := range cases { t.Logf("test case %d: %s", i, tc.Description) client, err := GetTestClient(tc.CredentialResponse) @@ -231,8 +234,8 @@ func TestRefreshableProvider_refresh(t *testing.T) { func TestRefreshableProvider_checkAndRefresh(t *testing.T) { cases := []struct { Description string - Expiration Time - ExpectedExpiration Time + Expiration types.Time + ExpectedExpiration types.Time CredentialResponse interface{} ShouldRefresh bool ExpectedError error diff --git a/internal/creds/types.go b/internal/creds/types.go index 01839b0..c293393 100644 --- a/internal/creds/types.go +++ b/internal/creds/types.go @@ -17,31 +17,23 @@ package creds import ( - "strconv" "sync" - "time" + "github.com/netflix/weep/internal/aws" "github.com/netflix/weep/internal/metadata" + "github.com/netflix/weep/internal/types" "github.com/aws/aws-sdk-go/aws/credentials" ) -type AwsCredentials struct { - AccessKeyId string `json:"AccessKeyId"` - SecretAccessKey string `json:"SecretAccessKey"` - SessionToken string `json:"SessionToken"` - Expiration Time `json:"Expiration"` - RoleArn string `json:"RoleArn"` -} - type RefreshableProvider struct { sync.RWMutex value credentials.Value client HTTPClient retries int retryDelay int - Expiration Time - LastRefreshed Time + Expiration types.Time + LastRefreshed types.Time Region string RoleName string RoleArn string @@ -58,7 +50,7 @@ type CredentialProcess struct { } type ConsolemeCredentialResponseType struct { - Credentials *AwsCredentials `json:"Credentials"` + Credentials *aws.Credentials `json:"Credentials"` } type ConsolemeCredentialRequestType struct { @@ -78,61 +70,12 @@ type ConsolemeCredentialErrorMessageType struct { RequestID string `json:"request_id"` } -type Time time.Time - -// MarshalJSON is used to convert the timestamp to JSON -func (t Time) MarshalJSON() ([]byte, error) { - return []byte(strconv.FormatInt(Time(t).Unix(), 10)), nil -} - -// UnmarshalJSON is used to convert the timestamp from JSON -func (t *Time) UnmarshalJSON(s []byte) (err error) { - r := string(s) - q, err := strconv.ParseInt(r, 10, 64) - if err != nil { - return err - } - *(*time.Time)(t) = time.Unix(q, 0) - return nil -} - -// Add returns t with the provided duration added to it. -func (t Time) Add(d time.Duration) time.Time { - return time.Time(t).Add(d) -} - -// Unix returns t as a Unix time, the number of seconds elapsed -// since January 1, 1970 UTC. The result does not depend on the -// location associated with t. -func (t Time) Unix() int64 { - return time.Time(t).Unix() -} - -func (t Time) UTC() time.Time { - return time.Time(t).UTC() -} - -// Format returns t as a timestamp string with the provided layout. -func (t Time) Format(layout string) string { - return time.Time(t).Format(layout) -} - -// Time returns the JSON time as a time.Time instance in UTC -func (t Time) Time() time.Time { - return time.Time(t).UTC() -} - -// String returns t as a formatted string -func (t Time) String() string { - return t.Time().String() -} - type Credentials struct { Role string NoIpRestrict bool - metaDataCredentials *AwsCredentials + metaDataCredentials *Credentials MetadataRegion string - LastRenewal Time + LastRenewal types.Time mu sync.Mutex } diff --git a/internal/swag/swag.go b/internal/swag/swag.go new file mode 100644 index 0000000..b233b76 --- /dev/null +++ b/internal/swag/swag.go @@ -0,0 +1,49 @@ +package swag + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/netflix/weep/internal/httpAuth/mtls" + "github.com/spf13/viper" +) + +type SwagResponse struct { + Name string `json:"name"` +} + +func getClient() (*http.Client, error) { + if viper.GetBool("swag.use_mtls") { + return mtls.NewHTTPClient() + } + return http.DefaultClient, nil +} + +func AccountName(id string) (string, error) { + client, err := getClient() + if err != nil { + return "", err + } + urlStr := fmt.Sprintf("%s/1/accounts/%s", viper.GetString("swag.url"), id) + req, err := http.NewRequest("GET", urlStr, nil) + if err != nil { + return "", err + } + resp, err := client.Do(req) + if err != nil { + return "", err + } + defer resp.Body.Close() + if resp.StatusCode == http.StatusOK { + dec := json.NewDecoder(resp.Body) + var r SwagResponse + err := dec.Decode(&r) + if err != nil { + return "", err + } + return r.Name, nil + } + + return "", nil +} diff --git a/internal/types/types.go b/internal/types/types.go new file mode 100644 index 0000000..e40df6b --- /dev/null +++ b/internal/types/types.go @@ -0,0 +1,55 @@ +package types + +import ( + "strconv" + "time" +) + +type Time time.Time + +// MarshalJSON is used to convert the timestamp to JSON +func (t Time) MarshalJSON() ([]byte, error) { + return []byte(strconv.FormatInt(Time(t).Unix(), 10)), nil +} + +// UnmarshalJSON is used to convert the timestamp from JSON +func (t *Time) UnmarshalJSON(s []byte) (err error) { + r := string(s) + q, err := strconv.ParseInt(r, 10, 64) + if err != nil { + return err + } + *(*time.Time)(t) = time.Unix(q, 0) + return nil +} + +// Add returns t with the provided duration added to it. +func (t Time) Add(d time.Duration) time.Time { + return time.Time(t).Add(d) +} + +// Unix returns t as a Unix time, the number of seconds elapsed +// since January 1, 1970 UTC. The result does not depend on the +// location associated with t. +func (t Time) Unix() int64 { + return time.Time(t).Unix() +} + +func (t Time) UTC() time.Time { + return time.Time(t).UTC() +} + +// Format returns t as a timestamp string with the provided layout. +func (t Time) Format(layout string) string { + return time.Time(t).Format(layout) +} + +// Time returns the JSON time as a time.Time instance in UTC +func (t Time) Time() time.Time { + return time.Time(t).UTC() +} + +// String returns t as a formatted string +func (t Time) String() string { + return t.Time().String() +}