|  | @@ -21,10 +21,12 @@ import (
 | 
	
		
			
				|  |  |  	"errors"
 | 
	
		
			
				|  |  |  	"io/ioutil"
 | 
	
		
			
				|  |  |  	"net/http"
 | 
	
		
			
				|  |  | +	"net/http/httptest"
 | 
	
		
			
				|  |  |  	"net/url"
 | 
	
		
			
				|  |  |  	"os"
 | 
	
		
			
				|  |  |  	"path/filepath"
 | 
	
		
			
				|  |  |  	"reflect"
 | 
	
		
			
				|  |  | +	"sync/atomic"
 | 
	
		
			
				|  |  |  	"testing"
 | 
	
		
			
				|  |  |  	"time"
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -36,7 +38,7 @@ import (
 | 
	
		
			
				|  |  |  	"golang.org/x/oauth2"
 | 
	
		
			
				|  |  |  )
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -func testLoginService(t *testing.T, m *MockAzureHelper) (*AzureLoginService, error) {
 | 
	
		
			
				|  |  | +func testLoginService(t *testing.T, apiHelperMock *MockAzureHelper, cloudEnvironmentSvc CloudEnvironmentService) (*azureLoginService, error) {
 | 
	
		
			
				|  |  |  	dir, err := ioutil.TempDir("", "test_store")
 | 
	
		
			
				|  |  |  	if err != nil {
 | 
	
		
			
				|  |  |  		return nil, err
 | 
	
	
		
			
				|  | @@ -44,20 +46,45 @@ func testLoginService(t *testing.T, m *MockAzureHelper) (*AzureLoginService, err
 | 
	
		
			
				|  |  |  	t.Cleanup(func() {
 | 
	
		
			
				|  |  |  		_ = os.RemoveAll(dir)
 | 
	
		
			
				|  |  |  	})
 | 
	
		
			
				|  |  | -	return newAzureLoginServiceFromPath(filepath.Join(dir, tokenStoreFilename), m)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	ces := CloudEnvironments
 | 
	
		
			
				|  |  | +	if cloudEnvironmentSvc != nil {
 | 
	
		
			
				|  |  | +		ces = cloudEnvironmentSvc
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	return newAzureLoginServiceFromPath(filepath.Join(dir, tokenStoreFilename), apiHelperMock, ces)
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  func TestRefreshInValidToken(t *testing.T) {
 | 
	
		
			
				|  |  | -	data := refreshTokenData("refreshToken")
 | 
	
		
			
				|  |  | -	m := &MockAzureHelper{}
 | 
	
		
			
				|  |  | -	m.On("queryToken", data, "123456").Return(azureToken{
 | 
	
		
			
				|  |  | +	data := url.Values{
 | 
	
		
			
				|  |  | +		"grant_type":    []string{"refresh_token"},
 | 
	
		
			
				|  |  | +		"client_id":     []string{clientID},
 | 
	
		
			
				|  |  | +		"scope":         []string{"offline_access https://management.docker.com/.default"},
 | 
	
		
			
				|  |  | +		"refresh_token": []string{"refreshToken"},
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	helperMock := &MockAzureHelper{}
 | 
	
		
			
				|  |  | +	helperMock.On("queryToken", mock.AnythingOfType("login.CloudEnvironment"), data, "123456").Return(azureToken{
 | 
	
		
			
				|  |  |  		RefreshToken: "newRefreshToken",
 | 
	
		
			
				|  |  |  		AccessToken:  "newAccessToken",
 | 
	
		
			
				|  |  |  		ExpiresIn:    3600,
 | 
	
		
			
				|  |  |  		Foci:         "1",
 | 
	
		
			
				|  |  |  	}, nil)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	azureLogin, err := testLoginService(t, m)
 | 
	
		
			
				|  |  | +	cloudEnvironmentSvcMock := &MockCloudEnvironmentService{}
 | 
	
		
			
				|  |  | +	cloudEnvironmentSvcMock.On("Get", "AzureDockerCloud").Return(CloudEnvironment{
 | 
	
		
			
				|  |  | +		Name: "AzureDockerCloud",
 | 
	
		
			
				|  |  | +		Authentication: CloudEnvironmentAuthentication{
 | 
	
		
			
				|  |  | +			LoginEndpoint: "https://login.docker.com",
 | 
	
		
			
				|  |  | +			Audiences: []string{
 | 
	
		
			
				|  |  | +				"https://management.docker.com",
 | 
	
		
			
				|  |  | +				"https://management-ext.docker.com",
 | 
	
		
			
				|  |  | +			},
 | 
	
		
			
				|  |  | +			Tenant: "common",
 | 
	
		
			
				|  |  | +		},
 | 
	
		
			
				|  |  | +		ResourceManagerURL: "https://management.docker.com",
 | 
	
		
			
				|  |  | +		Suffixes:           map[string]string{},
 | 
	
		
			
				|  |  | +	}, nil)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	azureLogin, err := testLoginService(t, helperMock, cloudEnvironmentSvcMock)
 | 
	
		
			
				|  |  |  	assert.NilError(t, err)
 | 
	
		
			
				|  |  |  	err = azureLogin.tokenStore.writeLoginInfo(TokenInfo{
 | 
	
		
			
				|  |  |  		TenantID: "123456",
 | 
	
	
		
			
				|  | @@ -67,33 +94,51 @@ func TestRefreshInValidToken(t *testing.T) {
 | 
	
		
			
				|  |  |  			Expiry:       time.Now().Add(-1 * time.Hour),
 | 
	
		
			
				|  |  |  			TokenType:    "Bearer",
 | 
	
		
			
				|  |  |  		},
 | 
	
		
			
				|  |  | +		CloudEnvironment: "AzureDockerCloud",
 | 
	
		
			
				|  |  |  	})
 | 
	
		
			
				|  |  |  	assert.NilError(t, err)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	token, _ := azureLogin.GetValidToken()
 | 
	
		
			
				|  |  | +	token, tenantID, err := azureLogin.GetValidToken()
 | 
	
		
			
				|  |  | +	assert.NilError(t, err)
 | 
	
		
			
				|  |  | +	assert.Equal(t, tenantID, "123456")
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	assert.Equal(t, token.AccessToken, "newAccessToken")
 | 
	
		
			
				|  |  |  	assert.Assert(t, time.Now().Add(3500*time.Second).Before(token.Expiry))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	storedToken, _ := azureLogin.tokenStore.readToken()
 | 
	
		
			
				|  |  | +	storedToken, err := azureLogin.tokenStore.readToken()
 | 
	
		
			
				|  |  | +	assert.NilError(t, err)
 | 
	
		
			
				|  |  |  	assert.Equal(t, storedToken.Token.AccessToken, "newAccessToken")
 | 
	
		
			
				|  |  |  	assert.Equal(t, storedToken.Token.RefreshToken, "newRefreshToken")
 | 
	
		
			
				|  |  |  	assert.Assert(t, time.Now().Add(3500*time.Second).Before(storedToken.Token.Expiry))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	assert.Equal(t, storedToken.CloudEnvironment, "AzureDockerCloud")
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -func TestClearErrorMessageIfNotAlreadyLoggedIn(t *testing.T) {
 | 
	
		
			
				|  |  | -	dir, err := ioutil.TempDir("", "test_store")
 | 
	
		
			
				|  |  | +func TestDoesNotRefreshValidToken(t *testing.T) {
 | 
	
		
			
				|  |  | +	expiryDate := time.Now().Add(1 * time.Hour)
 | 
	
		
			
				|  |  | +	azureLogin, err := testLoginService(t, nil, nil)
 | 
	
		
			
				|  |  |  	assert.NilError(t, err)
 | 
	
		
			
				|  |  | -	t.Cleanup(func() {
 | 
	
		
			
				|  |  | -		_ = os.RemoveAll(dir)
 | 
	
		
			
				|  |  | +	err = azureLogin.tokenStore.writeLoginInfo(TokenInfo{
 | 
	
		
			
				|  |  | +		TenantID: "123456",
 | 
	
		
			
				|  |  | +		Token: oauth2.Token{
 | 
	
		
			
				|  |  | +			AccessToken:  "accessToken",
 | 
	
		
			
				|  |  | +			RefreshToken: "refreshToken",
 | 
	
		
			
				|  |  | +			Expiry:       expiryDate,
 | 
	
		
			
				|  |  | +			TokenType:    "Bearer",
 | 
	
		
			
				|  |  | +		},
 | 
	
		
			
				|  |  | +		CloudEnvironment: AzurePublicCloudName,
 | 
	
		
			
				|  |  |  	})
 | 
	
		
			
				|  |  | -	_, err = newAuthorizerFromLoginStorePath(filepath.Join(dir, tokenStoreFilename))
 | 
	
		
			
				|  |  | -	assert.ErrorContains(t, err, "not logged in to azure, you need to run \"docker login azure\" first")
 | 
	
		
			
				|  |  | +	assert.NilError(t, err)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	token, tenantID, err := azureLogin.GetValidToken()
 | 
	
		
			
				|  |  | +	assert.NilError(t, err)
 | 
	
		
			
				|  |  | +	assert.Equal(t, token.AccessToken, "accessToken")
 | 
	
		
			
				|  |  | +	assert.Equal(t, tenantID, "123456")
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -func TestDoesNotRefreshValidToken(t *testing.T) {
 | 
	
		
			
				|  |  | +func TestTokenStoreAssumesAzurePublicCloud(t *testing.T) {
 | 
	
		
			
				|  |  |  	expiryDate := time.Now().Add(1 * time.Hour)
 | 
	
		
			
				|  |  | -	azureLogin, err := testLoginService(t, nil)
 | 
	
		
			
				|  |  | +	azureLogin, err := testLoginService(t, nil, nil)
 | 
	
		
			
				|  |  |  	assert.NilError(t, err)
 | 
	
		
			
				|  |  |  	err = azureLogin.tokenStore.writeLoginInfo(TokenInfo{
 | 
	
		
			
				|  |  |  		TenantID: "123456",
 | 
	
	
		
			
				|  | @@ -103,25 +148,33 @@ func TestDoesNotRefreshValidToken(t *testing.T) {
 | 
	
		
			
				|  |  |  			Expiry:       expiryDate,
 | 
	
		
			
				|  |  |  			TokenType:    "Bearer",
 | 
	
		
			
				|  |  |  		},
 | 
	
		
			
				|  |  | +		// Simulates upgrade from older version of Docker CLI that did not have cloud environment concept
 | 
	
		
			
				|  |  | +		CloudEnvironment: "",
 | 
	
		
			
				|  |  |  	})
 | 
	
		
			
				|  |  |  	assert.NilError(t, err)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	token, _ := azureLogin.GetValidToken()
 | 
	
		
			
				|  |  | +	token, tenantID, err := azureLogin.GetValidToken()
 | 
	
		
			
				|  |  | +	assert.NilError(t, err)
 | 
	
		
			
				|  |  | +	assert.Equal(t, tenantID, "123456")
 | 
	
		
			
				|  |  |  	assert.Equal(t, token.AccessToken, "accessToken")
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	ce, err := azureLogin.GetCloudEnvironment()
 | 
	
		
			
				|  |  | +	assert.NilError(t, err)
 | 
	
		
			
				|  |  | +	assert.Equal(t, ce.Name, AzurePublicCloudName)
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  func TestInvalidLogin(t *testing.T) {
 | 
	
		
			
				|  |  |  	m := &MockAzureHelper{}
 | 
	
		
			
				|  |  | -	m.On("openAzureLoginPage", mock.AnythingOfType("string")).Run(func(args mock.Arguments) {
 | 
	
		
			
				|  |  | +	m.On("openAzureLoginPage", mock.AnythingOfType("string"), mock.AnythingOfType("CloudEnvironment")).Run(func(args mock.Arguments) {
 | 
	
		
			
				|  |  |  		redirectURL := args.Get(0).(string)
 | 
	
		
			
				|  |  |  		err := queryKeyValue(redirectURL, "error", "access denied: login failed")
 | 
	
		
			
				|  |  |  		assert.NilError(t, err)
 | 
	
		
			
				|  |  |  	}).Return(nil)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	azureLogin, err := testLoginService(t, m)
 | 
	
		
			
				|  |  | +	azureLogin, err := testLoginService(t, m, nil)
 | 
	
		
			
				|  |  |  	assert.NilError(t, err)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	err = azureLogin.Login(context.TODO(), "")
 | 
	
		
			
				|  |  | +	err = azureLogin.Login(context.TODO(), "", AzurePublicCloudName)
 | 
	
		
			
				|  |  |  	assert.Error(t, err, "no login code: login failed")
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -129,19 +182,22 @@ func TestValidLogin(t *testing.T) {
 | 
	
		
			
				|  |  |  	var redirectURL string
 | 
	
		
			
				|  |  |  	ctx := context.TODO()
 | 
	
		
			
				|  |  |  	m := &MockAzureHelper{}
 | 
	
		
			
				|  |  | -	m.On("openAzureLoginPage", mock.AnythingOfType("string")).Run(func(args mock.Arguments) {
 | 
	
		
			
				|  |  | +	ce, err := CloudEnvironments.Get(AzurePublicCloudName)
 | 
	
		
			
				|  |  | +	assert.NilError(t, err)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	m.On("openAzureLoginPage", mock.AnythingOfType("string"), mock.AnythingOfType("CloudEnvironment")).Run(func(args mock.Arguments) {
 | 
	
		
			
				|  |  |  		redirectURL = args.Get(0).(string)
 | 
	
		
			
				|  |  |  		err := queryKeyValue(redirectURL, "code", "123456879")
 | 
	
		
			
				|  |  |  		assert.NilError(t, err)
 | 
	
		
			
				|  |  |  	}).Return(nil)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	m.On("queryToken", mock.MatchedBy(func(data url.Values) bool {
 | 
	
		
			
				|  |  | +	m.On("queryToken", mock.AnythingOfType("login.CloudEnvironment"), mock.MatchedBy(func(data url.Values) bool {
 | 
	
		
			
				|  |  |  		//Need a matcher here because the value of redirectUrl is not known until executing openAzureLoginPage
 | 
	
		
			
				|  |  |  		return reflect.DeepEqual(data, url.Values{
 | 
	
		
			
				|  |  |  			"grant_type":   []string{"authorization_code"},
 | 
	
		
			
				|  |  |  			"client_id":    []string{clientID},
 | 
	
		
			
				|  |  |  			"code":         []string{"123456879"},
 | 
	
		
			
				|  |  | -			"scope":        []string{scopes},
 | 
	
		
			
				|  |  | +			"scope":        []string{ce.GetTokenScope()},
 | 
	
		
			
				|  |  |  			"redirect_uri": []string{redirectURL},
 | 
	
		
			
				|  |  |  		})
 | 
	
		
			
				|  |  |  	}), "organizations").Return(azureToken{
 | 
	
	
		
			
				|  | @@ -153,18 +209,18 @@ func TestValidLogin(t *testing.T) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	authBody := `{"value":[{"id":"/tenants/12345a7c-c56d-43e8-9549-dd230ce8a038","tenantId":"12345a7c-c56d-43e8-9549-dd230ce8a038"}]}`
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	m.On("queryAPIWithHeader", ctx, getTenantURL, "Bearer firstAccessToken").Return([]byte(authBody), 200, nil)
 | 
	
		
			
				|  |  | -	data := refreshTokenData("firstRefreshToken")
 | 
	
		
			
				|  |  | -	m.On("queryToken", data, "12345a7c-c56d-43e8-9549-dd230ce8a038").Return(azureToken{
 | 
	
		
			
				|  |  | +	m.On("queryAPIWithHeader", ctx, ce.GetTenantQueryURL(), "Bearer firstAccessToken").Return([]byte(authBody), 200, nil)
 | 
	
		
			
				|  |  | +	data := refreshTokenData("firstRefreshToken", ce)
 | 
	
		
			
				|  |  | +	m.On("queryToken", mock.AnythingOfType("login.CloudEnvironment"), data, "12345a7c-c56d-43e8-9549-dd230ce8a038").Return(azureToken{
 | 
	
		
			
				|  |  |  		RefreshToken: "newRefreshToken",
 | 
	
		
			
				|  |  |  		AccessToken:  "newAccessToken",
 | 
	
		
			
				|  |  |  		ExpiresIn:    3600,
 | 
	
		
			
				|  |  |  		Foci:         "1",
 | 
	
		
			
				|  |  |  	}, nil)
 | 
	
		
			
				|  |  | -	azureLogin, err := testLoginService(t, m)
 | 
	
		
			
				|  |  | +	azureLogin, err := testLoginService(t, m, nil)
 | 
	
		
			
				|  |  |  	assert.NilError(t, err)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	err = azureLogin.Login(ctx, "")
 | 
	
		
			
				|  |  | +	err = azureLogin.Login(ctx, "", AzurePublicCloudName)
 | 
	
		
			
				|  |  |  	assert.NilError(t, err)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	loginToken, err := azureLogin.tokenStore.readToken()
 | 
	
	
		
			
				|  | @@ -174,24 +230,28 @@ func TestValidLogin(t *testing.T) {
 | 
	
		
			
				|  |  |  	assert.Assert(t, time.Now().Add(3500*time.Second).Before(loginToken.Token.Expiry))
 | 
	
		
			
				|  |  |  	assert.Equal(t, loginToken.TenantID, "12345a7c-c56d-43e8-9549-dd230ce8a038")
 | 
	
		
			
				|  |  |  	assert.Equal(t, loginToken.Token.Type(), "Bearer")
 | 
	
		
			
				|  |  | +	assert.Equal(t, loginToken.CloudEnvironment, "AzureCloud")
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  func TestValidLoginRequestedTenant(t *testing.T) {
 | 
	
		
			
				|  |  |  	var redirectURL string
 | 
	
		
			
				|  |  |  	m := &MockAzureHelper{}
 | 
	
		
			
				|  |  | -	m.On("openAzureLoginPage", mock.AnythingOfType("string")).Run(func(args mock.Arguments) {
 | 
	
		
			
				|  |  | +	ce, err := CloudEnvironments.Get(AzurePublicCloudName)
 | 
	
		
			
				|  |  | +	assert.NilError(t, err)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	m.On("openAzureLoginPage", mock.AnythingOfType("string"), mock.AnythingOfType("CloudEnvironment")).Run(func(args mock.Arguments) {
 | 
	
		
			
				|  |  |  		redirectURL = args.Get(0).(string)
 | 
	
		
			
				|  |  |  		err := queryKeyValue(redirectURL, "code", "123456879")
 | 
	
		
			
				|  |  |  		assert.NilError(t, err)
 | 
	
		
			
				|  |  |  	}).Return(nil)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	m.On("queryToken", mock.MatchedBy(func(data url.Values) bool {
 | 
	
		
			
				|  |  | +	m.On("queryToken", mock.AnythingOfType("login.CloudEnvironment"), mock.MatchedBy(func(data url.Values) bool {
 | 
	
		
			
				|  |  |  		//Need a matcher here because the value of redirectUrl is not known until executing openAzureLoginPage
 | 
	
		
			
				|  |  |  		return reflect.DeepEqual(data, url.Values{
 | 
	
		
			
				|  |  |  			"grant_type":   []string{"authorization_code"},
 | 
	
		
			
				|  |  |  			"client_id":    []string{clientID},
 | 
	
		
			
				|  |  |  			"code":         []string{"123456879"},
 | 
	
		
			
				|  |  | -			"scope":        []string{scopes},
 | 
	
		
			
				|  |  | +			"scope":        []string{ce.GetTokenScope()},
 | 
	
		
			
				|  |  |  			"redirect_uri": []string{redirectURL},
 | 
	
		
			
				|  |  |  		})
 | 
	
		
			
				|  |  |  	}), "organizations").Return(azureToken{
 | 
	
	
		
			
				|  | @@ -205,18 +265,18 @@ func TestValidLoginRequestedTenant(t *testing.T) {
 | 
	
		
			
				|  |  |  						   {"id":"/tenants/12345a7c-c56d-43e8-9549-dd230ce8a038","tenantId":"12345a7c-c56d-43e8-9549-dd230ce8a038"}]}`
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	ctx := context.TODO()
 | 
	
		
			
				|  |  | -	m.On("queryAPIWithHeader", ctx, getTenantURL, "Bearer firstAccessToken").Return([]byte(authBody), 200, nil)
 | 
	
		
			
				|  |  | -	data := refreshTokenData("firstRefreshToken")
 | 
	
		
			
				|  |  | -	m.On("queryToken", data, "12345a7c-c56d-43e8-9549-dd230ce8a038").Return(azureToken{
 | 
	
		
			
				|  |  | +	m.On("queryAPIWithHeader", ctx, ce.GetTenantQueryURL(), "Bearer firstAccessToken").Return([]byte(authBody), 200, nil)
 | 
	
		
			
				|  |  | +	data := refreshTokenData("firstRefreshToken", ce)
 | 
	
		
			
				|  |  | +	m.On("queryToken", mock.AnythingOfType("login.CloudEnvironment"), data, "12345a7c-c56d-43e8-9549-dd230ce8a038").Return(azureToken{
 | 
	
		
			
				|  |  |  		RefreshToken: "newRefreshToken",
 | 
	
		
			
				|  |  |  		AccessToken:  "newAccessToken",
 | 
	
		
			
				|  |  |  		ExpiresIn:    3600,
 | 
	
		
			
				|  |  |  		Foci:         "1",
 | 
	
		
			
				|  |  |  	}, nil)
 | 
	
		
			
				|  |  | -	azureLogin, err := testLoginService(t, m)
 | 
	
		
			
				|  |  | +	azureLogin, err := testLoginService(t, m, nil)
 | 
	
		
			
				|  |  |  	assert.NilError(t, err)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	err = azureLogin.Login(ctx, "12345a7c-c56d-43e8-9549-dd230ce8a038")
 | 
	
		
			
				|  |  | +	err = azureLogin.Login(ctx, "12345a7c-c56d-43e8-9549-dd230ce8a038", AzurePublicCloudName)
 | 
	
		
			
				|  |  |  	assert.NilError(t, err)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	loginToken, err := azureLogin.tokenStore.readToken()
 | 
	
	
		
			
				|  | @@ -226,24 +286,28 @@ func TestValidLoginRequestedTenant(t *testing.T) {
 | 
	
		
			
				|  |  |  	assert.Assert(t, time.Now().Add(3500*time.Second).Before(loginToken.Token.Expiry))
 | 
	
		
			
				|  |  |  	assert.Equal(t, loginToken.TenantID, "12345a7c-c56d-43e8-9549-dd230ce8a038")
 | 
	
		
			
				|  |  |  	assert.Equal(t, loginToken.Token.Type(), "Bearer")
 | 
	
		
			
				|  |  | +	assert.Equal(t, loginToken.CloudEnvironment, "AzureCloud")
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  func TestLoginNoTenant(t *testing.T) {
 | 
	
		
			
				|  |  |  	var redirectURL string
 | 
	
		
			
				|  |  |  	m := &MockAzureHelper{}
 | 
	
		
			
				|  |  | -	m.On("openAzureLoginPage", mock.AnythingOfType("string")).Run(func(args mock.Arguments) {
 | 
	
		
			
				|  |  | +	ce, err := CloudEnvironments.Get(AzurePublicCloudName)
 | 
	
		
			
				|  |  | +	assert.NilError(t, err)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	m.On("openAzureLoginPage", mock.AnythingOfType("string"), mock.AnythingOfType("CloudEnvironment")).Run(func(args mock.Arguments) {
 | 
	
		
			
				|  |  |  		redirectURL = args.Get(0).(string)
 | 
	
		
			
				|  |  |  		err := queryKeyValue(redirectURL, "code", "123456879")
 | 
	
		
			
				|  |  |  		assert.NilError(t, err)
 | 
	
		
			
				|  |  |  	}).Return(nil)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	m.On("queryToken", mock.MatchedBy(func(data url.Values) bool {
 | 
	
		
			
				|  |  | +	m.On("queryToken", mock.AnythingOfType("login.CloudEnvironment"), mock.MatchedBy(func(data url.Values) bool {
 | 
	
		
			
				|  |  |  		//Need a matcher here because the value of redirectUrl is not known until executing openAzureLoginPage
 | 
	
		
			
				|  |  |  		return reflect.DeepEqual(data, url.Values{
 | 
	
		
			
				|  |  |  			"grant_type":   []string{"authorization_code"},
 | 
	
		
			
				|  |  |  			"client_id":    []string{clientID},
 | 
	
		
			
				|  |  |  			"code":         []string{"123456879"},
 | 
	
		
			
				|  |  | -			"scope":        []string{scopes},
 | 
	
		
			
				|  |  | +			"scope":        []string{ce.GetTokenScope()},
 | 
	
		
			
				|  |  |  			"redirect_uri": []string{redirectURL},
 | 
	
		
			
				|  |  |  		})
 | 
	
		
			
				|  |  |  	}), "organizations").Return(azureToken{
 | 
	
	
		
			
				|  | @@ -255,31 +319,34 @@ func TestLoginNoTenant(t *testing.T) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	ctx := context.TODO()
 | 
	
		
			
				|  |  |  	authBody := `{"value":[{"id":"/tenants/12345a7c-c56d-43e8-9549-dd230ce8a038","tenantId":"12345a7c-c56d-43e8-9549-dd230ce8a038"}]}`
 | 
	
		
			
				|  |  | -	m.On("queryAPIWithHeader", ctx, getTenantURL, "Bearer firstAccessToken").Return([]byte(authBody), 200, nil)
 | 
	
		
			
				|  |  | +	m.On("queryAPIWithHeader", ctx, ce.GetTenantQueryURL(), "Bearer firstAccessToken").Return([]byte(authBody), 200, nil)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	azureLogin, err := testLoginService(t, m)
 | 
	
		
			
				|  |  | +	azureLogin, err := testLoginService(t, m, nil)
 | 
	
		
			
				|  |  |  	assert.NilError(t, err)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	err = azureLogin.Login(ctx, "00000000-c56d-43e8-9549-dd230ce8a038")
 | 
	
		
			
				|  |  | +	err = azureLogin.Login(ctx, "00000000-c56d-43e8-9549-dd230ce8a038", AzurePublicCloudName)
 | 
	
		
			
				|  |  |  	assert.Error(t, err, "could not find requested azure tenant 00000000-c56d-43e8-9549-dd230ce8a038: login failed")
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  func TestLoginRequestedTenantNotFound(t *testing.T) {
 | 
	
		
			
				|  |  |  	var redirectURL string
 | 
	
		
			
				|  |  |  	m := &MockAzureHelper{}
 | 
	
		
			
				|  |  | -	m.On("openAzureLoginPage", mock.AnythingOfType("string")).Run(func(args mock.Arguments) {
 | 
	
		
			
				|  |  | +	ce, err := CloudEnvironments.Get(AzurePublicCloudName)
 | 
	
		
			
				|  |  | +	assert.NilError(t, err)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	m.On("openAzureLoginPage", mock.AnythingOfType("string"), mock.AnythingOfType("CloudEnvironment")).Run(func(args mock.Arguments) {
 | 
	
		
			
				|  |  |  		redirectURL = args.Get(0).(string)
 | 
	
		
			
				|  |  |  		err := queryKeyValue(redirectURL, "code", "123456879")
 | 
	
		
			
				|  |  |  		assert.NilError(t, err)
 | 
	
		
			
				|  |  |  	}).Return(nil)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	m.On("queryToken", mock.MatchedBy(func(data url.Values) bool {
 | 
	
		
			
				|  |  | +	m.On("queryToken", mock.AnythingOfType("login.CloudEnvironment"), mock.MatchedBy(func(data url.Values) bool {
 | 
	
		
			
				|  |  |  		//Need a matcher here because the value of redirectUrl is not known until executing openAzureLoginPage
 | 
	
		
			
				|  |  |  		return reflect.DeepEqual(data, url.Values{
 | 
	
		
			
				|  |  |  			"grant_type":   []string{"authorization_code"},
 | 
	
		
			
				|  |  |  			"client_id":    []string{clientID},
 | 
	
		
			
				|  |  |  			"code":         []string{"123456879"},
 | 
	
		
			
				|  |  | -			"scope":        []string{scopes},
 | 
	
		
			
				|  |  | +			"scope":        []string{ce.GetTokenScope()},
 | 
	
		
			
				|  |  |  			"redirect_uri": []string{redirectURL},
 | 
	
		
			
				|  |  |  		})
 | 
	
		
			
				|  |  |  	}), "organizations").Return(azureToken{
 | 
	
	
		
			
				|  | @@ -291,31 +358,34 @@ func TestLoginRequestedTenantNotFound(t *testing.T) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	ctx := context.TODO()
 | 
	
		
			
				|  |  |  	authBody := `{"value":[]}`
 | 
	
		
			
				|  |  | -	m.On("queryAPIWithHeader", ctx, getTenantURL, "Bearer firstAccessToken").Return([]byte(authBody), 200, nil)
 | 
	
		
			
				|  |  | +	m.On("queryAPIWithHeader", ctx, ce.GetTenantQueryURL(), "Bearer firstAccessToken").Return([]byte(authBody), 200, nil)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	azureLogin, err := testLoginService(t, m)
 | 
	
		
			
				|  |  | +	azureLogin, err := testLoginService(t, m, nil)
 | 
	
		
			
				|  |  |  	assert.NilError(t, err)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	err = azureLogin.Login(ctx, "")
 | 
	
		
			
				|  |  | +	err = azureLogin.Login(ctx, "", AzurePublicCloudName)
 | 
	
		
			
				|  |  |  	assert.Error(t, err, "could not find azure tenant: login failed")
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  func TestLoginAuthorizationFailed(t *testing.T) {
 | 
	
		
			
				|  |  |  	var redirectURL string
 | 
	
		
			
				|  |  |  	m := &MockAzureHelper{}
 | 
	
		
			
				|  |  | -	m.On("openAzureLoginPage", mock.AnythingOfType("string")).Run(func(args mock.Arguments) {
 | 
	
		
			
				|  |  | +	ce, err := CloudEnvironments.Get(AzurePublicCloudName)
 | 
	
		
			
				|  |  | +	assert.NilError(t, err)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	m.On("openAzureLoginPage", mock.AnythingOfType("string"), mock.AnythingOfType("CloudEnvironment")).Run(func(args mock.Arguments) {
 | 
	
		
			
				|  |  |  		redirectURL = args.Get(0).(string)
 | 
	
		
			
				|  |  |  		err := queryKeyValue(redirectURL, "code", "123456879")
 | 
	
		
			
				|  |  |  		assert.NilError(t, err)
 | 
	
		
			
				|  |  |  	}).Return(nil)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	m.On("queryToken", mock.MatchedBy(func(data url.Values) bool {
 | 
	
		
			
				|  |  | +	m.On("queryToken", mock.AnythingOfType("login.CloudEnvironment"), mock.MatchedBy(func(data url.Values) bool {
 | 
	
		
			
				|  |  |  		//Need a matcher here because the value of redirectUrl is not known until executing openAzureLoginPage
 | 
	
		
			
				|  |  |  		return reflect.DeepEqual(data, url.Values{
 | 
	
		
			
				|  |  |  			"grant_type":   []string{"authorization_code"},
 | 
	
		
			
				|  |  |  			"client_id":    []string{clientID},
 | 
	
		
			
				|  |  |  			"code":         []string{"123456879"},
 | 
	
		
			
				|  |  | -			"scope":        []string{scopes},
 | 
	
		
			
				|  |  | +			"scope":        []string{ce.GetTokenScope()},
 | 
	
		
			
				|  |  |  			"redirect_uri": []string{redirectURL},
 | 
	
		
			
				|  |  |  		})
 | 
	
		
			
				|  |  |  	}), "organizations").Return(azureToken{
 | 
	
	
		
			
				|  | @@ -328,35 +398,38 @@ func TestLoginAuthorizationFailed(t *testing.T) {
 | 
	
		
			
				|  |  |  	authBody := `[access denied]`
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	ctx := context.TODO()
 | 
	
		
			
				|  |  | -	m.On("queryAPIWithHeader", ctx, getTenantURL, "Bearer firstAccessToken").Return([]byte(authBody), 400, nil)
 | 
	
		
			
				|  |  | +	m.On("queryAPIWithHeader", ctx, ce.GetTenantQueryURL(), "Bearer firstAccessToken").Return([]byte(authBody), 400, nil)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	azureLogin, err := testLoginService(t, m)
 | 
	
		
			
				|  |  | +	azureLogin, err := testLoginService(t, m, nil)
 | 
	
		
			
				|  |  |  	assert.NilError(t, err)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	err = azureLogin.Login(ctx, "")
 | 
	
		
			
				|  |  | +	err = azureLogin.Login(ctx, "", AzurePublicCloudName)
 | 
	
		
			
				|  |  |  	assert.Error(t, err, "unable to login status code 400: [access denied]: login failed")
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  func TestValidThroughDeviceCodeFlow(t *testing.T) {
 | 
	
		
			
				|  |  |  	m := &MockAzureHelper{}
 | 
	
		
			
				|  |  | -	m.On("openAzureLoginPage", mock.AnythingOfType("string")).Return(errors.New("Could not open browser"))
 | 
	
		
			
				|  |  | -	m.On("getDeviceCodeFlowToken").Return(adal.Token{AccessToken: "firstAccessToken", RefreshToken: "firstRefreshToken"}, nil)
 | 
	
		
			
				|  |  | +	ce, err := CloudEnvironments.Get(AzurePublicCloudName)
 | 
	
		
			
				|  |  | +	assert.NilError(t, err)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	m.On("openAzureLoginPage", mock.AnythingOfType("string"), mock.AnythingOfType("CloudEnvironment")).Return(errors.New("Could not open browser"))
 | 
	
		
			
				|  |  | +	m.On("getDeviceCodeFlowToken", mock.AnythingOfType("CloudEnvironment")).Return(adal.Token{AccessToken: "firstAccessToken", RefreshToken: "firstRefreshToken"}, nil)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	authBody := `{"value":[{"id":"/tenants/12345a7c-c56d-43e8-9549-dd230ce8a038","tenantId":"12345a7c-c56d-43e8-9549-dd230ce8a038"}]}`
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	ctx := context.TODO()
 | 
	
		
			
				|  |  | -	m.On("queryAPIWithHeader", ctx, getTenantURL, "Bearer firstAccessToken").Return([]byte(authBody), 200, nil)
 | 
	
		
			
				|  |  | -	data := refreshTokenData("firstRefreshToken")
 | 
	
		
			
				|  |  | -	m.On("queryToken", data, "12345a7c-c56d-43e8-9549-dd230ce8a038").Return(azureToken{
 | 
	
		
			
				|  |  | +	m.On("queryAPIWithHeader", ctx, ce.GetTenantQueryURL(), "Bearer firstAccessToken").Return([]byte(authBody), 200, nil)
 | 
	
		
			
				|  |  | +	data := refreshTokenData("firstRefreshToken", ce)
 | 
	
		
			
				|  |  | +	m.On("queryToken", mock.AnythingOfType("login.CloudEnvironment"), data, "12345a7c-c56d-43e8-9549-dd230ce8a038").Return(azureToken{
 | 
	
		
			
				|  |  |  		RefreshToken: "newRefreshToken",
 | 
	
		
			
				|  |  |  		AccessToken:  "newAccessToken",
 | 
	
		
			
				|  |  |  		ExpiresIn:    3600,
 | 
	
		
			
				|  |  |  		Foci:         "1",
 | 
	
		
			
				|  |  |  	}, nil)
 | 
	
		
			
				|  |  | -	azureLogin, err := testLoginService(t, m)
 | 
	
		
			
				|  |  | +	azureLogin, err := testLoginService(t, m, nil)
 | 
	
		
			
				|  |  |  	assert.NilError(t, err)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	err = azureLogin.Login(ctx, "")
 | 
	
		
			
				|  |  | +	err = azureLogin.Login(ctx, "", AzurePublicCloudName)
 | 
	
		
			
				|  |  |  	assert.NilError(t, err)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	loginToken, err := azureLogin.tokenStore.readToken()
 | 
	
	
		
			
				|  | @@ -366,13 +439,110 @@ func TestValidThroughDeviceCodeFlow(t *testing.T) {
 | 
	
		
			
				|  |  |  	assert.Assert(t, time.Now().Add(3500*time.Second).Before(loginToken.Token.Expiry))
 | 
	
		
			
				|  |  |  	assert.Equal(t, loginToken.TenantID, "12345a7c-c56d-43e8-9549-dd230ce8a038")
 | 
	
		
			
				|  |  |  	assert.Equal(t, loginToken.Token.Type(), "Bearer")
 | 
	
		
			
				|  |  | +	assert.Equal(t, loginToken.CloudEnvironment, "AzureCloud")
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -func refreshTokenData(refreshToken string) url.Values {
 | 
	
		
			
				|  |  | +func TestNonstandardCloudEnvironment(t *testing.T) {
 | 
	
		
			
				|  |  | +	dockerCloudMetadata := []byte(`
 | 
	
		
			
				|  |  | +	[{
 | 
	
		
			
				|  |  | +		"authentication": {
 | 
	
		
			
				|  |  | +			"loginEndpoint": "https://login.docker.com/",
 | 
	
		
			
				|  |  | +			"audiences": [
 | 
	
		
			
				|  |  | +				"https://management.docker.com/",
 | 
	
		
			
				|  |  | +				"https://management.cli.docker.com/"
 | 
	
		
			
				|  |  | +			],
 | 
	
		
			
				|  |  | +			"tenant": "F5773994-FE88-482E-9E33-6E799D250416"
 | 
	
		
			
				|  |  | +		},
 | 
	
		
			
				|  |  | +		"name": "AzureDockerCloud",
 | 
	
		
			
				|  |  | +		"suffixes": {
 | 
	
		
			
				|  |  | +			"acrLoginServer": "azurecr.docker.io"
 | 
	
		
			
				|  |  | +		},
 | 
	
		
			
				|  |  | +		"resourceManager": "https://management.docker.com/"
 | 
	
		
			
				|  |  | +	}]`)
 | 
	
		
			
				|  |  | +	var metadataReqCount int32 = 0
 | 
	
		
			
				|  |  | +	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
	
		
			
				|  |  | +		_, err := w.Write(dockerCloudMetadata)
 | 
	
		
			
				|  |  | +		assert.NilError(t, err)
 | 
	
		
			
				|  |  | +		atomic.AddInt32(&metadataReqCount, 1)
 | 
	
		
			
				|  |  | +	}))
 | 
	
		
			
				|  |  | +	defer srv.Close()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	cloudMetadataURL, cloudMetadataURLSet := os.LookupEnv(CloudMetadataURLVar)
 | 
	
		
			
				|  |  | +	if cloudMetadataURLSet {
 | 
	
		
			
				|  |  | +		defer func() {
 | 
	
		
			
				|  |  | +			err := os.Setenv(CloudMetadataURLVar, cloudMetadataURL)
 | 
	
		
			
				|  |  | +			assert.NilError(t, err)
 | 
	
		
			
				|  |  | +		}()
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	err := os.Setenv(CloudMetadataURLVar, srv.URL)
 | 
	
		
			
				|  |  | +	assert.NilError(t, err)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	ctx := context.TODO()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	ces := newCloudEnvironmentService()
 | 
	
		
			
				|  |  | +	ces.cloudMetadataURL = srv.URL
 | 
	
		
			
				|  |  | +	dockerCloudEnv, err := ces.Get("AzureDockerCloud")
 | 
	
		
			
				|  |  | +	assert.NilError(t, err)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	helperMock := &MockAzureHelper{}
 | 
	
		
			
				|  |  | +	var redirectURL string
 | 
	
		
			
				|  |  | +	helperMock.On("openAzureLoginPage", mock.AnythingOfType("string"), mock.AnythingOfType("CloudEnvironment")).Run(func(args mock.Arguments) {
 | 
	
		
			
				|  |  | +		redirectURL = args.Get(0).(string)
 | 
	
		
			
				|  |  | +		err := queryKeyValue(redirectURL, "code", "123456879")
 | 
	
		
			
				|  |  | +		assert.NilError(t, err)
 | 
	
		
			
				|  |  | +	}).Return(nil)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	helperMock.On("queryToken", mock.AnythingOfType("login.CloudEnvironment"), mock.MatchedBy(func(data url.Values) bool {
 | 
	
		
			
				|  |  | +		//Need a matcher here because the value of redirectUrl is not known until executing openAzureLoginPage
 | 
	
		
			
				|  |  | +		return reflect.DeepEqual(data, url.Values{
 | 
	
		
			
				|  |  | +			"grant_type":   []string{"authorization_code"},
 | 
	
		
			
				|  |  | +			"client_id":    []string{clientID},
 | 
	
		
			
				|  |  | +			"code":         []string{"123456879"},
 | 
	
		
			
				|  |  | +			"scope":        []string{dockerCloudEnv.GetTokenScope()},
 | 
	
		
			
				|  |  | +			"redirect_uri": []string{redirectURL},
 | 
	
		
			
				|  |  | +		})
 | 
	
		
			
				|  |  | +	}), "organizations").Return(azureToken{
 | 
	
		
			
				|  |  | +		RefreshToken: "firstRefreshToken",
 | 
	
		
			
				|  |  | +		AccessToken:  "firstAccessToken",
 | 
	
		
			
				|  |  | +		ExpiresIn:    3600,
 | 
	
		
			
				|  |  | +		Foci:         "1",
 | 
	
		
			
				|  |  | +	}, nil)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	authBody := `{"value":[{"id":"/tenants/F5773994-FE88-482E-9E33-6E799D250416","tenantId":"F5773994-FE88-482E-9E33-6E799D250416"}]}`
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	helperMock.On("queryAPIWithHeader", ctx, dockerCloudEnv.GetTenantQueryURL(), "Bearer firstAccessToken").Return([]byte(authBody), 200, nil)
 | 
	
		
			
				|  |  | +	data := refreshTokenData("firstRefreshToken", dockerCloudEnv)
 | 
	
		
			
				|  |  | +	helperMock.On("queryToken", mock.AnythingOfType("login.CloudEnvironment"), data, "F5773994-FE88-482E-9E33-6E799D250416").Return(azureToken{
 | 
	
		
			
				|  |  | +		RefreshToken: "newRefreshToken",
 | 
	
		
			
				|  |  | +		AccessToken:  "newAccessToken",
 | 
	
		
			
				|  |  | +		ExpiresIn:    3600,
 | 
	
		
			
				|  |  | +		Foci:         "1",
 | 
	
		
			
				|  |  | +	}, nil)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	azureLogin, err := testLoginService(t, helperMock, ces)
 | 
	
		
			
				|  |  | +	assert.NilError(t, err)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	err = azureLogin.Login(ctx, "", "AzureDockerCloud")
 | 
	
		
			
				|  |  | +	assert.NilError(t, err)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	loginToken, err := azureLogin.tokenStore.readToken()
 | 
	
		
			
				|  |  | +	assert.NilError(t, err)
 | 
	
		
			
				|  |  | +	assert.Equal(t, loginToken.Token.AccessToken, "newAccessToken")
 | 
	
		
			
				|  |  | +	assert.Equal(t, loginToken.Token.RefreshToken, "newRefreshToken")
 | 
	
		
			
				|  |  | +	assert.Assert(t, time.Now().Add(3500*time.Second).Before(loginToken.Token.Expiry))
 | 
	
		
			
				|  |  | +	assert.Equal(t, loginToken.TenantID, "F5773994-FE88-482E-9E33-6E799D250416")
 | 
	
		
			
				|  |  | +	assert.Equal(t, loginToken.Token.Type(), "Bearer")
 | 
	
		
			
				|  |  | +	assert.Equal(t, loginToken.CloudEnvironment, "AzureDockerCloud")
 | 
	
		
			
				|  |  | +	assert.Equal(t, metadataReqCount, int32(1))
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// Don't warn about refreshToken parameter taking the same value for all invocations
 | 
	
		
			
				|  |  | +// nolint:unparam
 | 
	
		
			
				|  |  | +func refreshTokenData(refreshToken string, ce CloudEnvironment) url.Values {
 | 
	
		
			
				|  |  |  	return url.Values{
 | 
	
		
			
				|  |  |  		"grant_type":    []string{"refresh_token"},
 | 
	
		
			
				|  |  |  		"client_id":     []string{clientID},
 | 
	
		
			
				|  |  | -		"scope":         []string{scopes},
 | 
	
		
			
				|  |  | +		"scope":         []string{ce.GetTokenScope()},
 | 
	
		
			
				|  |  |  		"refresh_token": []string{refreshToken},
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  }
 | 
	
	
		
			
				|  | @@ -394,13 +564,13 @@ type MockAzureHelper struct {
 | 
	
		
			
				|  |  |  	mock.Mock
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -func (s *MockAzureHelper) getDeviceCodeFlowToken() (adal.Token, error) {
 | 
	
		
			
				|  |  | -	args := s.Called()
 | 
	
		
			
				|  |  | +func (s *MockAzureHelper) getDeviceCodeFlowToken(ce CloudEnvironment) (adal.Token, error) {
 | 
	
		
			
				|  |  | +	args := s.Called(ce)
 | 
	
		
			
				|  |  |  	return args.Get(0).(adal.Token), args.Error(1)
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -func (s *MockAzureHelper) queryToken(data url.Values, tenantID string) (token azureToken, err error) {
 | 
	
		
			
				|  |  | -	args := s.Called(data, tenantID)
 | 
	
		
			
				|  |  | +func (s *MockAzureHelper) queryToken(ce CloudEnvironment, data url.Values, tenantID string) (token azureToken, err error) {
 | 
	
		
			
				|  |  | +	args := s.Called(ce, data, tenantID)
 | 
	
		
			
				|  |  |  	return args.Get(0).(azureToken), args.Error(1)
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -409,7 +579,16 @@ func (s *MockAzureHelper) queryAPIWithHeader(ctx context.Context, authorizationU
 | 
	
		
			
				|  |  |  	return args.Get(0).([]byte), args.Int(1), args.Error(2)
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -func (s *MockAzureHelper) openAzureLoginPage(redirectURL string) error {
 | 
	
		
			
				|  |  | -	args := s.Called(redirectURL)
 | 
	
		
			
				|  |  | +func (s *MockAzureHelper) openAzureLoginPage(redirectURL string, ce CloudEnvironment) error {
 | 
	
		
			
				|  |  | +	args := s.Called(redirectURL, ce)
 | 
	
		
			
				|  |  |  	return args.Error(0)
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +type MockCloudEnvironmentService struct {
 | 
	
		
			
				|  |  | +	mock.Mock
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +func (s *MockCloudEnvironmentService) Get(name string) (CloudEnvironment, error) {
 | 
	
		
			
				|  |  | +	args := s.Called(name)
 | 
	
		
			
				|  |  | +	return args.Get(0).(CloudEnvironment), args.Error(1)
 | 
	
		
			
				|  |  | +}
 |