From a04d94c019d87ddc60e19a63764fcf5b38c32c77 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Fri, 8 Sep 2017 19:54:53 +0300 Subject: [PATCH 1/5] Fixing AzureCLI / CloudShell authentication - Hard-coding the value for `expiresIn` due to the Azure CLI and CloudShell authentication models having different types - Updating `expiresOn` to be the difference in seconds between the baseDate & now - rather than a date - Parsing the AzureCLI time out in the local system timezone, given it's saved in local time but with no timezone info --- autorest/adal/cli.go | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/autorest/adal/cli.go b/autorest/adal/cli.go index 524ab38b3..4a6eeb79b 100644 --- a/autorest/adal/cli.go +++ b/autorest/adal/cli.go @@ -1,8 +1,12 @@ package adal import ( - "github.com/mitchellh/go-homedir" + "fmt" + "log" "strconv" + "time" + + "github.com/mitchellh/go-homedir" ) // AzureCLIToken represents an AccessToken from the Azure CLI @@ -10,7 +14,6 @@ type AzureCLIToken struct { AccessToken string `json:"accessToken"` Authority string `json:"_authority"` ClientID string `json:"_clientId"` - ExpiresIn int `json:"expiresIn"` ExpiresOn string `json:"expiresOn"` IdentityProvider string `json:"identityProvider"` IsMRRT bool `json:"isMRRT"` @@ -47,13 +50,42 @@ func AzureCLIProfilePath() (string, error) { } // ToToken converts an AzureCLIToken to a Token -func (t AzureCLIToken) ToToken() Token { - return Token{ +func (t AzureCLIToken) ToToken() (*Token, error) { + tokenExpirationDate, err := ParseAzureCLIExpirationDate(t.ExpiresOn) + if err != nil { + return nil, fmt.Errorf("Error parsing Token Expiration Date %q: %+v", t.ExpiresOn, err) + } + + difference := tokenExpirationDate.Sub(expirationBase) + seconds := difference.Seconds() + + token := Token{ AccessToken: t.AccessToken, Type: t.TokenType, - ExpiresIn: strconv.Itoa(t.ExpiresIn), - ExpiresOn: t.ExpiresOn, + ExpiresIn: "3600", + ExpiresOn: strconv.Itoa(int(seconds)), RefreshToken: t.RefreshToken, Resource: t.Resource, } + return &token, nil +} + +// ParseAzureCLIExpirationDate parses either a Azure CLI or CloudShell date into a time object +func ParseAzureCLIExpirationDate(input string) (*time.Time, error) { + log.Printf("[DEBUG] Token Date: %s", input) + + // CloudShell (and potentially the Azure CLI in future) + expirationDate, cloudShellErr := time.Parse(time.RFC3339, input) + if cloudShellErr != nil { + // Azure CLI (Python) e.g. 2017-08-31 19:48:57.998857 (plus the local timezone) + cliFormat := "2006-01-02 15:04:05.999999" + expirationDate, cliErr := time.ParseInLocation(cliFormat, input, time.Local) + if cliErr == nil { + return &expirationDate, nil + } + + return nil, fmt.Errorf("Error parsing expiration date %q.\n\nCloudShell Error: \n%+v\n\nCLI Error:\n%+v", input, cloudShellErr, cliErr) + } + + return &expirationDate, nil } From 575eb82bdf668f4400d9a9cf89f1f1c0de892877 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Tue, 12 Sep 2017 12:12:27 +0200 Subject: [PATCH 2/5] Changes requested in review --- autorest/adal/cli.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/autorest/adal/cli.go b/autorest/adal/cli.go index 4a6eeb79b..cbf9ebbee 100644 --- a/autorest/adal/cli.go +++ b/autorest/adal/cli.go @@ -2,7 +2,6 @@ package adal import ( "fmt" - "log" "strconv" "time" @@ -57,13 +56,11 @@ func (t AzureCLIToken) ToToken() (*Token, error) { } difference := tokenExpirationDate.Sub(expirationBase) - seconds := difference.Seconds() - token := Token{ AccessToken: t.AccessToken, Type: t.TokenType, ExpiresIn: "3600", - ExpiresOn: strconv.Itoa(int(seconds)), + ExpiresOn: strconv.Itoa(int(difference.Seconds())), RefreshToken: t.RefreshToken, Resource: t.Resource, } @@ -72,8 +69,6 @@ func (t AzureCLIToken) ToToken() (*Token, error) { // ParseAzureCLIExpirationDate parses either a Azure CLI or CloudShell date into a time object func ParseAzureCLIExpirationDate(input string) (*time.Time, error) { - log.Printf("[DEBUG] Token Date: %s", input) - // CloudShell (and potentially the Azure CLI in future) expirationDate, cloudShellErr := time.Parse(time.RFC3339, input) if cloudShellErr != nil { From ede361e578943f306ccba04264d0889c72829481 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Tue, 12 Sep 2017 19:35:28 +0200 Subject: [PATCH 3/5] Refactoring the CLI stuff into it's own package --- autorest/adal/cli/persist.go | 47 ++++++++++++++++++++++++++ autorest/adal/cli/profile.go | 24 +++++++++++++ autorest/adal/{cli.go => cli/token.go} | 40 ++++++---------------- autorest/adal/persist.go | 39 --------------------- autorest/adal/token.go | 6 ++-- 5 files changed, 84 insertions(+), 72 deletions(-) create mode 100644 autorest/adal/cli/persist.go create mode 100644 autorest/adal/cli/profile.go rename autorest/adal/{cli.go => cli/token.go} (69%) diff --git a/autorest/adal/cli/persist.go b/autorest/adal/cli/persist.go new file mode 100644 index 000000000..29d5c743a --- /dev/null +++ b/autorest/adal/cli/persist.go @@ -0,0 +1,47 @@ +package cli + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "os" + + "github.com/dimchansky/utfbom" +) + +// LoadCLIProfile restores an AzureCLIProfile object from a file located at 'path'. +func LoadCLIProfile(path string) (AzureCLIProfile, error) { + var profile AzureCLIProfile + + contents, err := ioutil.ReadFile(path) + if err != nil { + return profile, fmt.Errorf("failed to open file (%s) while loading token: %v", path, err) + } + reader := utfbom.SkipOnly(bytes.NewReader(contents)) + + dec := json.NewDecoder(reader) + if err = dec.Decode(&profile); err != nil { + return profile, fmt.Errorf("failed to decode contents of file (%s) into a AzureCLIProfile representation: %v", path, err) + } + + return profile, nil +} + +// LoadCLITokens restores a set of AzureCLIToken objects from a file located at 'path'. +func LoadCLITokens(path string) ([]AzureCLIToken, error) { + file, err := os.Open(path) + if err != nil { + return nil, fmt.Errorf("failed to open file (%s) while loading token: %v", path, err) + } + defer file.Close() + + var tokens []AzureCLIToken + + dec := json.NewDecoder(file) + if err = dec.Decode(&tokens); err != nil { + return nil, fmt.Errorf("failed to decode contents of file (%s) into a AzureCLIToken representation: %v", path, err) + } + + return tokens, nil +} diff --git a/autorest/adal/cli/profile.go b/autorest/adal/cli/profile.go new file mode 100644 index 000000000..94f4696c1 --- /dev/null +++ b/autorest/adal/cli/profile.go @@ -0,0 +1,24 @@ +package cli + +import "github.com/mitchellh/go-homedir" + +// AzureCLIProfile represents a Profile from the Azure CLI +type AzureCLIProfile struct { + InstallationID string `json:"installationId"` + Subscriptions []AzureCLISubscription `json:"subscriptions"` +} + +// AzureCLISubscription represents a Subscription from the Azure CLI +type AzureCLISubscription struct { + EnvironmentName string `json:"environmentName"` + ID string `json:"id"` + IsDefault bool `json:"isDefault"` + Name string `json:"name"` + State string `json:"state"` + TenantID string `json:"tenantId"` +} + +// AzureCLIProfilePath returns the path where the Azure Profile is stored from the Azure CLI +func AzureCLIProfilePath() (string, error) { + return homedir.Expand("~/.azure/azureProfile.json") +} diff --git a/autorest/adal/cli.go b/autorest/adal/cli/token.go similarity index 69% rename from autorest/adal/cli.go rename to autorest/adal/cli/token.go index cbf9ebbee..017189a1a 100644 --- a/autorest/adal/cli.go +++ b/autorest/adal/cli/token.go @@ -1,10 +1,11 @@ -package adal +package cli import ( "fmt" "strconv" "time" + "github.com/Azure/go-autorest/autorest/adal" "github.com/mitchellh/go-homedir" ) @@ -22,41 +23,15 @@ type AzureCLIToken struct { UserID string `json:"userId"` } -// AzureCLIProfile represents a Profile from the Azure CLI -type AzureCLIProfile struct { - InstallationID string `json:"installationId"` - Subscriptions []AzureCLISubscription `json:"subscriptions"` -} - -// AzureCLISubscription represents a Subscription from the Azure CLI -type AzureCLISubscription struct { - EnvironmentName string `json:"environmentName"` - ID string `json:"id"` - IsDefault bool `json:"isDefault"` - Name string `json:"name"` - State string `json:"state"` - TenantID string `json:"tenantId"` -} - -// AzureCLIAccessTokensPath returns the path where access tokens are stored from the Azure CLI -func AzureCLIAccessTokensPath() (string, error) { - return homedir.Expand("~/.azure/accessTokens.json") -} - -// AzureCLIProfilePath returns the path where the Azure Profile is stored from the Azure CLI -func AzureCLIProfilePath() (string, error) { - return homedir.Expand("~/.azure/azureProfile.json") -} - // ToToken converts an AzureCLIToken to a Token -func (t AzureCLIToken) ToToken() (*Token, error) { +func (t AzureCLIToken) ToToken() (*adal.Token, error) { tokenExpirationDate, err := ParseAzureCLIExpirationDate(t.ExpiresOn) if err != nil { return nil, fmt.Errorf("Error parsing Token Expiration Date %q: %+v", t.ExpiresOn, err) } - difference := tokenExpirationDate.Sub(expirationBase) - token := Token{ + difference := tokenExpirationDate.Sub(adal.ExpirationBase) + token := adal.Token{ AccessToken: t.AccessToken, Type: t.TokenType, ExpiresIn: "3600", @@ -67,6 +42,11 @@ func (t AzureCLIToken) ToToken() (*Token, error) { return &token, nil } +// AzureCLIAccessTokensPath returns the path where access tokens are stored from the Azure CLI +func AzureCLIAccessTokensPath() (string, error) { + return homedir.Expand("~/.azure/accessTokens.json") +} + // ParseAzureCLIExpirationDate parses either a Azure CLI or CloudShell date into a time object func ParseAzureCLIExpirationDate(input string) (*time.Time, error) { // CloudShell (and potentially the Azure CLI in future) diff --git a/autorest/adal/persist.go b/autorest/adal/persist.go index 2ce2667dd..73711c667 100644 --- a/autorest/adal/persist.go +++ b/autorest/adal/persist.go @@ -1,14 +1,11 @@ package adal import ( - "bytes" "encoding/json" "fmt" "io/ioutil" "os" "path/filepath" - - "github.com/dimchansky/utfbom" ) // LoadToken restores a Token object from a file located at 'path'. @@ -28,42 +25,6 @@ func LoadToken(path string) (*Token, error) { return &token, nil } -// LoadCLITokens restores a set of AzureCLIToken objects from a file located at 'path'. -func LoadCLITokens(path string) ([]AzureCLIToken, error) { - file, err := os.Open(path) - if err != nil { - return nil, fmt.Errorf("failed to open file (%s) while loading token: %v", path, err) - } - defer file.Close() - - var tokens []AzureCLIToken - - dec := json.NewDecoder(file) - if err = dec.Decode(&tokens); err != nil { - return nil, fmt.Errorf("failed to decode contents of file (%s) into a AzureCLIToken representation: %v", path, err) - } - - return tokens, nil -} - -// LoadCLIProfile restores an AzureCLIProfile object from a file located at 'path'. -func LoadCLIProfile(path string) (AzureCLIProfile, error) { - var profile AzureCLIProfile - - contents, err := ioutil.ReadFile(path) - if err != nil { - return profile, fmt.Errorf("failed to open file (%s) while loading token: %v", path, err) - } - reader := utfbom.SkipOnly(bytes.NewReader(contents)) - - dec := json.NewDecoder(reader) - if err = dec.Decode(&profile); err != nil { - return profile, fmt.Errorf("failed to decode contents of file (%s) into a AzureCLIProfile representation: %v", path, err) - } - - return profile, nil -} - // SaveToken persists an oauth token at the given location on disk. // It moves the new file into place so it can safely be used to replace an existing file // that maybe accessed by multiple processes. diff --git a/autorest/adal/token.go b/autorest/adal/token.go index 559fc6653..5e6ac648e 100644 --- a/autorest/adal/token.go +++ b/autorest/adal/token.go @@ -35,10 +35,10 @@ const ( managedIdentitySettingsPath = "/var/lib/waagent/ManagedIdentity-Settings" ) -var expirationBase time.Time +var ExpirationBase time.Time func init() { - expirationBase, _ = time.Parse(time.RFC3339, tokenBaseDate) + ExpirationBase, _ = time.Parse(time.RFC3339, tokenBaseDate) } // OAuthTokenProvider is an interface which should be implemented by an access token retriever @@ -76,7 +76,7 @@ func (t Token) Expires() time.Time { if err != nil { s = -3600 } - return expirationBase.Add(time.Duration(s) * time.Second).UTC() + return ExpirationBase.Add(time.Duration(s) * time.Second).UTC() } // IsExpired returns true if the Token is expired, false otherwise. From a32ff8d05c48430765889e9016afdaa261260090 Mon Sep 17 00:00:00 2001 From: Martin Strobel Date: Wed, 13 Sep 2017 12:24:18 -0700 Subject: [PATCH 4/5] Refactoring a little --- autorest/adal/token.go | 13 ++++------- autorest/adal/token_test.go | 9 +++---- autorest/{adal => azure}/cli/persist.go | 8 +++---- autorest/{adal => azure}/cli/profile.go | 0 autorest/{adal => azure}/cli/token.go | 31 ++++++++++++++----------- 5 files changed, 31 insertions(+), 30 deletions(-) rename autorest/{adal => azure}/cli/persist.go (81%) rename autorest/{adal => azure}/cli/profile.go (100%) rename autorest/{adal => azure}/cli/token.go (63%) diff --git a/autorest/adal/token.go b/autorest/adal/token.go index 5e6ac648e..4152426d6 100644 --- a/autorest/adal/token.go +++ b/autorest/adal/token.go @@ -15,12 +15,12 @@ import ( "strings" "time" + "github.com/Azure/go-autorest/autorest/date" "github.com/dgrijalva/jwt-go" ) const ( defaultRefresh = 5 * time.Minute - tokenBaseDate = "1970-01-01T00:00:00Z" // OAuthGrantTypeDeviceCode is the "grant_type" identifier used in device flow OAuthGrantTypeDeviceCode = "device_code" @@ -35,12 +35,6 @@ const ( managedIdentitySettingsPath = "/var/lib/waagent/ManagedIdentity-Settings" ) -var ExpirationBase time.Time - -func init() { - ExpirationBase, _ = time.Parse(time.RFC3339, tokenBaseDate) -} - // OAuthTokenProvider is an interface which should be implemented by an access token retriever type OAuthTokenProvider interface { OAuthToken() string @@ -76,7 +70,10 @@ func (t Token) Expires() time.Time { if err != nil { s = -3600 } - return ExpirationBase.Add(time.Duration(s) * time.Second).UTC() + + expiration := date.NewUnixTimeFromSeconds(float64(s)) + + return time.Time(expiration).UTC() } // IsExpired returns true if the Token is expired, false otherwise. diff --git a/autorest/adal/token_test.go b/autorest/adal/token_test.go index 9c92f4198..bec7a79a6 100644 --- a/autorest/adal/token_test.go +++ b/autorest/adal/token_test.go @@ -17,6 +17,7 @@ import ( "testing" "time" + "github.com/Azure/go-autorest/autorest/date" "github.com/Azure/go-autorest/autorest/mocks" ) @@ -342,7 +343,7 @@ func TestServicePrincipalTokenRefreshReturnsErrorIfNotOk(t *testing.T) { func TestServicePrincipalTokenRefreshUnmarshals(t *testing.T) { spt := newServicePrincipalToken() - expiresOn := strconv.Itoa(int(time.Now().Add(3600 * time.Second).Sub(expirationBase).Seconds())) + expiresOn := strconv.Itoa(int(time.Now().Add(3600 * time.Second).Sub(date.UnixEpoch()).Seconds())) j := newTokenJSON(expiresOn, "resource") resp := mocks.NewResponseWithContent(j) c := mocks.NewSender() @@ -430,7 +431,7 @@ func TestRefreshCallback(t *testing.T) { return nil }) - expiresOn := strconv.Itoa(int(time.Now().Add(3600 * time.Second).Sub(expirationBase).Seconds())) + expiresOn := strconv.Itoa(int(time.Now().Add(3600 * time.Second).Sub(date.UnixEpoch()).Seconds())) sender := mocks.NewSender() j := newTokenJSON(expiresOn, "resource") @@ -451,7 +452,7 @@ func TestRefreshCallbackErrorPropagates(t *testing.T) { return fmt.Errorf(errorText) }) - expiresOn := strconv.Itoa(int(time.Now().Add(3600 * time.Second).Sub(expirationBase).Seconds())) + expiresOn := strconv.Itoa(int(time.Now().Add(3600 * time.Second).Sub(date.UnixEpoch()).Seconds())) sender := mocks.NewSender() j := newTokenJSON(expiresOn, "resource") @@ -554,7 +555,7 @@ func expireToken(t *Token) *Token { func setTokenToExpireAt(t *Token, expireAt time.Time) *Token { t.ExpiresIn = "3600" - t.ExpiresOn = strconv.Itoa(int(expireAt.Sub(expirationBase).Seconds())) + t.ExpiresOn = strconv.Itoa(int(expireAt.Sub(date.UnixEpoch()).Seconds())) t.NotBefore = t.ExpiresOn return t } diff --git a/autorest/adal/cli/persist.go b/autorest/azure/cli/persist.go similarity index 81% rename from autorest/adal/cli/persist.go rename to autorest/azure/cli/persist.go index 29d5c743a..d5bcfc63c 100644 --- a/autorest/adal/cli/persist.go +++ b/autorest/azure/cli/persist.go @@ -28,19 +28,19 @@ func LoadCLIProfile(path string) (AzureCLIProfile, error) { return profile, nil } -// LoadCLITokens restores a set of AzureCLIToken objects from a file located at 'path'. -func LoadCLITokens(path string) ([]AzureCLIToken, error) { +// LoadCLITokens restores a set of Token objects from a file located at 'path'. +func LoadCLITokens(path string) ([]Token, error) { file, err := os.Open(path) if err != nil { return nil, fmt.Errorf("failed to open file (%s) while loading token: %v", path, err) } defer file.Close() - var tokens []AzureCLIToken + var tokens []Token dec := json.NewDecoder(file) if err = dec.Decode(&tokens); err != nil { - return nil, fmt.Errorf("failed to decode contents of file (%s) into a AzureCLIToken representation: %v", path, err) + return nil, fmt.Errorf("failed to decode contents of file (%s) into a `cli.Token` representation: %v", path, err) } return tokens, nil diff --git a/autorest/adal/cli/profile.go b/autorest/azure/cli/profile.go similarity index 100% rename from autorest/adal/cli/profile.go rename to autorest/azure/cli/profile.go diff --git a/autorest/adal/cli/token.go b/autorest/azure/cli/token.go similarity index 63% rename from autorest/adal/cli/token.go rename to autorest/azure/cli/token.go index 017189a1a..21c6333b1 100644 --- a/autorest/adal/cli/token.go +++ b/autorest/azure/cli/token.go @@ -6,11 +6,12 @@ import ( "time" "github.com/Azure/go-autorest/autorest/adal" + "github.com/Azure/go-autorest/autorest/date" "github.com/mitchellh/go-homedir" ) -// AzureCLIToken represents an AccessToken from the Azure CLI -type AzureCLIToken struct { +// Token represents an AccessToken from the Azure CLI +type Token struct { AccessToken string `json:"accessToken"` Authority string `json:"_authority"` ClientID string `json:"_clientId"` @@ -23,15 +24,17 @@ type AzureCLIToken struct { UserID string `json:"userId"` } -// ToToken converts an AzureCLIToken to a Token -func (t AzureCLIToken) ToToken() (*adal.Token, error) { - tokenExpirationDate, err := ParseAzureCLIExpirationDate(t.ExpiresOn) +// ToADALToken converts an Azure CLI `Token`` to an `adal.Token`` +func (t Token) ToADALToken() (converted adal.Token, err error) { + tokenExpirationDate, err := ParseExpirationDate(t.ExpiresOn) if err != nil { - return nil, fmt.Errorf("Error parsing Token Expiration Date %q: %+v", t.ExpiresOn, err) + err = fmt.Errorf("Error parsing Token Expiration Date %q: %+v", t.ExpiresOn, err) + return } - difference := tokenExpirationDate.Sub(adal.ExpirationBase) - token := adal.Token{ + difference := tokenExpirationDate.Sub(date.UnixEpoch()) + + converted = adal.Token{ AccessToken: t.AccessToken, Type: t.TokenType, ExpiresIn: "3600", @@ -39,21 +42,21 @@ func (t AzureCLIToken) ToToken() (*adal.Token, error) { RefreshToken: t.RefreshToken, Resource: t.Resource, } - return &token, nil + return } -// AzureCLIAccessTokensPath returns the path where access tokens are stored from the Azure CLI -func AzureCLIAccessTokensPath() (string, error) { +// AccessTokensPath returns the path where access tokens are stored from the Azure CLI +func AccessTokensPath() (string, error) { return homedir.Expand("~/.azure/accessTokens.json") } -// ParseAzureCLIExpirationDate parses either a Azure CLI or CloudShell date into a time object -func ParseAzureCLIExpirationDate(input string) (*time.Time, error) { +// ParseExpirationDate parses either a Azure CLI or CloudShell date into a time object +func ParseExpirationDate(input string) (*time.Time, error) { // CloudShell (and potentially the Azure CLI in future) expirationDate, cloudShellErr := time.Parse(time.RFC3339, input) if cloudShellErr != nil { // Azure CLI (Python) e.g. 2017-08-31 19:48:57.998857 (plus the local timezone) - cliFormat := "2006-01-02 15:04:05.999999" + const cliFormat = "2006-01-02 15:04:05.999999" expirationDate, cliErr := time.ParseInLocation(cliFormat, input, time.Local) if cliErr == nil { return &expirationDate, nil From 954fd09c06a70a37a4c06c87a6bfdd86b646d583 Mon Sep 17 00:00:00 2001 From: Martin Strobel Date: Wed, 13 Sep 2017 12:50:56 -0700 Subject: [PATCH 5/5] Even more refactoring. --- autorest/azure/cli/persist.go | 47 ----------------------------------- autorest/azure/cli/profile.go | 45 ++++++++++++++++++++++++++------- autorest/azure/cli/token.go | 20 +++++++++++++++ 3 files changed, 56 insertions(+), 56 deletions(-) delete mode 100644 autorest/azure/cli/persist.go diff --git a/autorest/azure/cli/persist.go b/autorest/azure/cli/persist.go deleted file mode 100644 index d5bcfc63c..000000000 --- a/autorest/azure/cli/persist.go +++ /dev/null @@ -1,47 +0,0 @@ -package cli - -import ( - "bytes" - "encoding/json" - "fmt" - "io/ioutil" - "os" - - "github.com/dimchansky/utfbom" -) - -// LoadCLIProfile restores an AzureCLIProfile object from a file located at 'path'. -func LoadCLIProfile(path string) (AzureCLIProfile, error) { - var profile AzureCLIProfile - - contents, err := ioutil.ReadFile(path) - if err != nil { - return profile, fmt.Errorf("failed to open file (%s) while loading token: %v", path, err) - } - reader := utfbom.SkipOnly(bytes.NewReader(contents)) - - dec := json.NewDecoder(reader) - if err = dec.Decode(&profile); err != nil { - return profile, fmt.Errorf("failed to decode contents of file (%s) into a AzureCLIProfile representation: %v", path, err) - } - - return profile, nil -} - -// LoadCLITokens restores a set of Token objects from a file located at 'path'. -func LoadCLITokens(path string) ([]Token, error) { - file, err := os.Open(path) - if err != nil { - return nil, fmt.Errorf("failed to open file (%s) while loading token: %v", path, err) - } - defer file.Close() - - var tokens []Token - - dec := json.NewDecoder(file) - if err = dec.Decode(&tokens); err != nil { - return nil, fmt.Errorf("failed to decode contents of file (%s) into a `cli.Token` representation: %v", path, err) - } - - return tokens, nil -} diff --git a/autorest/azure/cli/profile.go b/autorest/azure/cli/profile.go index 94f4696c1..b5b897c7d 100644 --- a/autorest/azure/cli/profile.go +++ b/autorest/azure/cli/profile.go @@ -1,15 +1,23 @@ package cli -import "github.com/mitchellh/go-homedir" +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" -// AzureCLIProfile represents a Profile from the Azure CLI -type AzureCLIProfile struct { - InstallationID string `json:"installationId"` - Subscriptions []AzureCLISubscription `json:"subscriptions"` + "github.com/dimchansky/utfbom" + "github.com/mitchellh/go-homedir" +) + +// Profile represents a Profile from the Azure CLI +type Profile struct { + InstallationID string `json:"installationId"` + Subscriptions []Subscription `json:"subscriptions"` } -// AzureCLISubscription represents a Subscription from the Azure CLI -type AzureCLISubscription struct { +// Subscription represents a Subscription from the Azure CLI +type Subscription struct { EnvironmentName string `json:"environmentName"` ID string `json:"id"` IsDefault bool `json:"isDefault"` @@ -18,7 +26,26 @@ type AzureCLISubscription struct { TenantID string `json:"tenantId"` } -// AzureCLIProfilePath returns the path where the Azure Profile is stored from the Azure CLI -func AzureCLIProfilePath() (string, error) { +// ProfilePath returns the path where the Azure Profile is stored from the Azure CLI +func ProfilePath() (string, error) { return homedir.Expand("~/.azure/azureProfile.json") } + +// LoadProfile restores a Profile object from a file located at 'path'. +func LoadProfile(path string) (result Profile, err error) { + var contents []byte + contents, err = ioutil.ReadFile(path) + if err != nil { + err = fmt.Errorf("failed to open file (%s) while loading token: %v", path, err) + return + } + reader := utfbom.SkipOnly(bytes.NewReader(contents)) + + dec := json.NewDecoder(reader) + if err = dec.Decode(&result); err != nil { + err = fmt.Errorf("failed to decode contents of file (%s) into a Profile representation: %v", path, err) + return + } + + return +} diff --git a/autorest/azure/cli/token.go b/autorest/azure/cli/token.go index 21c6333b1..a1f3af151 100644 --- a/autorest/azure/cli/token.go +++ b/autorest/azure/cli/token.go @@ -1,7 +1,9 @@ package cli import ( + "encoding/json" "fmt" + "os" "strconv" "time" @@ -67,3 +69,21 @@ func ParseExpirationDate(input string) (*time.Time, error) { return &expirationDate, nil } + +// LoadTokens restores a set of Token objects from a file located at 'path'. +func LoadTokens(path string) ([]Token, error) { + file, err := os.Open(path) + if err != nil { + return nil, fmt.Errorf("failed to open file (%s) while loading token: %v", path, err) + } + defer file.Close() + + var tokens []Token + + dec := json.NewDecoder(file) + if err = dec.Decode(&tokens); err != nil { + return nil, fmt.Errorf("failed to decode contents of file (%s) into a `cli.Token` representation: %v", path, err) + } + + return tokens, nil +}