2020-06-18 16:13:24 +02:00
|
|
|
/*
|
|
|
|
Copyright 2020 Docker, Inc.
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2020-05-12 17:26:11 +02:00
|
|
|
package login
|
|
|
|
|
|
|
|
import (
|
2020-05-14 22:54:57 +02:00
|
|
|
"context"
|
2020-05-12 17:26:11 +02:00
|
|
|
"io/ioutil"
|
2020-05-14 22:54:57 +02:00
|
|
|
"net/http"
|
2020-05-12 17:26:11 +02:00
|
|
|
"net/url"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2020-05-14 22:54:57 +02:00
|
|
|
"reflect"
|
2020-05-12 17:26:11 +02:00
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/mock"
|
2020-07-31 15:22:34 +02:00
|
|
|
"gotest.tools/v3/assert"
|
2020-05-12 17:26:11 +02:00
|
|
|
|
|
|
|
"golang.org/x/oauth2"
|
|
|
|
)
|
|
|
|
|
2020-07-31 15:25:32 +02:00
|
|
|
func testLoginService(t *testing.T, m *MockAzureHelper) (*AzureLoginService, error) {
|
2020-05-12 17:26:11 +02:00
|
|
|
dir, err := ioutil.TempDir("", "test_store")
|
2020-07-31 15:22:34 +02:00
|
|
|
if err != nil {
|
2020-07-31 15:25:32 +02:00
|
|
|
return nil, err
|
2020-07-31 15:22:34 +02:00
|
|
|
}
|
|
|
|
t.Cleanup(func() {
|
|
|
|
_ = os.RemoveAll(dir)
|
|
|
|
})
|
|
|
|
return newAzureLoginServiceFromPath(filepath.Join(dir, tokenStoreFilename), m)
|
2020-05-12 17:26:11 +02:00
|
|
|
}
|
|
|
|
|
2020-07-31 15:22:34 +02:00
|
|
|
func TestRefreshInValidToken(t *testing.T) {
|
2020-05-14 22:54:57 +02:00
|
|
|
data := refreshTokenData("refreshToken")
|
2020-07-31 15:22:34 +02:00
|
|
|
m := &MockAzureHelper{}
|
|
|
|
m.On("queryToken", data, "123456").Return(azureToken{
|
2020-05-12 17:26:11 +02:00
|
|
|
RefreshToken: "newRefreshToken",
|
|
|
|
AccessToken: "newAccessToken",
|
|
|
|
ExpiresIn: 3600,
|
|
|
|
Foci: "1",
|
|
|
|
}, nil)
|
|
|
|
|
2020-07-31 15:22:34 +02:00
|
|
|
azureLogin, err := testLoginService(t, m)
|
|
|
|
assert.NilError(t, err)
|
|
|
|
err = azureLogin.tokenStore.writeLoginInfo(TokenInfo{
|
2020-05-12 17:26:11 +02:00
|
|
|
TenantID: "123456",
|
|
|
|
Token: oauth2.Token{
|
|
|
|
AccessToken: "accessToken",
|
|
|
|
RefreshToken: "refreshToken",
|
|
|
|
Expiry: time.Now().Add(-1 * time.Hour),
|
|
|
|
TokenType: "Bearer",
|
|
|
|
},
|
|
|
|
})
|
2020-07-31 15:22:34 +02:00
|
|
|
assert.NilError(t, err)
|
2020-05-12 17:26:11 +02:00
|
|
|
|
2020-07-31 15:22:34 +02:00
|
|
|
token, _ := azureLogin.GetValidToken()
|
2020-05-12 17:26:11 +02:00
|
|
|
|
2020-07-31 15:22:34 +02:00
|
|
|
assert.Equal(t, token.AccessToken, "newAccessToken")
|
|
|
|
assert.Assert(t, time.Now().Add(3500*time.Second).Before(token.Expiry))
|
2020-05-12 17:26:11 +02:00
|
|
|
|
2020-07-31 15:22:34 +02:00
|
|
|
storedToken, _ := azureLogin.tokenStore.readToken()
|
|
|
|
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))
|
2020-05-12 17:26:11 +02:00
|
|
|
}
|
|
|
|
|
2020-07-31 15:22:34 +02:00
|
|
|
func TestClearErrorMessageIfNotAlreadyLoggedIn(t *testing.T) {
|
|
|
|
dir, err := ioutil.TempDir("", "test_store")
|
|
|
|
assert.NilError(t, err)
|
|
|
|
t.Cleanup(func() {
|
|
|
|
_ = os.RemoveAll(dir)
|
|
|
|
})
|
|
|
|
_, err = newAuthorizerFromLoginStorePath(filepath.Join(dir, tokenStoreFilename))
|
|
|
|
assert.ErrorContains(t, err, "not logged in to azure, you need to run \"docker login azure\" first")
|
2020-06-19 10:59:39 +02:00
|
|
|
}
|
|
|
|
|
2020-07-31 15:22:34 +02:00
|
|
|
func TestDoesNotRefreshValidToken(t *testing.T) {
|
2020-05-12 17:26:11 +02:00
|
|
|
expiryDate := time.Now().Add(1 * time.Hour)
|
2020-07-31 15:22:34 +02:00
|
|
|
azureLogin, err := testLoginService(t, nil)
|
|
|
|
assert.NilError(t, err)
|
|
|
|
err = azureLogin.tokenStore.writeLoginInfo(TokenInfo{
|
2020-05-12 17:26:11 +02:00
|
|
|
TenantID: "123456",
|
|
|
|
Token: oauth2.Token{
|
|
|
|
AccessToken: "accessToken",
|
|
|
|
RefreshToken: "refreshToken",
|
|
|
|
Expiry: expiryDate,
|
|
|
|
TokenType: "Bearer",
|
|
|
|
},
|
|
|
|
})
|
2020-07-31 15:22:34 +02:00
|
|
|
assert.NilError(t, err)
|
2020-05-12 17:26:11 +02:00
|
|
|
|
2020-07-31 15:22:34 +02:00
|
|
|
token, _ := azureLogin.GetValidToken()
|
|
|
|
assert.Equal(t, token.AccessToken, "accessToken")
|
2020-05-12 17:26:11 +02:00
|
|
|
}
|
|
|
|
|
2020-07-31 15:22:34 +02:00
|
|
|
func TestInvalidLogin(t *testing.T) {
|
|
|
|
m := &MockAzureHelper{}
|
|
|
|
m.On("openAzureLoginPage", mock.AnythingOfType("string")).Run(func(args mock.Arguments) {
|
2020-05-14 22:54:57 +02:00
|
|
|
redirectURL := args.Get(0).(string)
|
2020-06-04 10:20:39 +02:00
|
|
|
err := queryKeyValue(redirectURL, "error", "access denied: login failed")
|
2020-07-31 15:22:34 +02:00
|
|
|
assert.NilError(t, err)
|
2020-05-14 22:54:57 +02:00
|
|
|
})
|
|
|
|
|
2020-07-31 15:22:34 +02:00
|
|
|
azureLogin, err := testLoginService(t, m)
|
|
|
|
assert.NilError(t, err)
|
2020-05-14 22:54:57 +02:00
|
|
|
|
2020-07-01 12:25:18 +02:00
|
|
|
err = azureLogin.Login(context.TODO(), "")
|
2020-07-31 15:22:34 +02:00
|
|
|
assert.Error(t, err, "no login code: login failed")
|
2020-05-14 22:54:57 +02:00
|
|
|
}
|
|
|
|
|
2020-07-31 15:22:34 +02:00
|
|
|
func TestValidLogin(t *testing.T) {
|
2020-05-14 22:54:57 +02:00
|
|
|
var redirectURL string
|
2020-07-31 15:22:34 +02:00
|
|
|
m := &MockAzureHelper{}
|
|
|
|
m.On("openAzureLoginPage", mock.AnythingOfType("string")).Run(func(args mock.Arguments) {
|
2020-05-14 22:54:57 +02:00
|
|
|
redirectURL = args.Get(0).(string)
|
|
|
|
err := queryKeyValue(redirectURL, "code", "123456879")
|
2020-07-31 15:22:34 +02:00
|
|
|
assert.NilError(t, err)
|
2020-05-14 22:54:57 +02:00
|
|
|
})
|
|
|
|
|
2020-07-31 15:22:34 +02:00
|
|
|
m.On("queryToken", mock.MatchedBy(func(data url.Values) bool {
|
2020-05-14 22:54:57 +02:00
|
|
|
//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},
|
|
|
|
"redirect_uri": []string{redirectURL},
|
|
|
|
})
|
|
|
|
}), "organizations").Return(azureToken{
|
|
|
|
RefreshToken: "firstRefreshToken",
|
|
|
|
AccessToken: "firstAccessToken",
|
|
|
|
ExpiresIn: 3600,
|
|
|
|
Foci: "1",
|
|
|
|
}, nil)
|
|
|
|
|
|
|
|
authBody := `{"value":[{"id":"/tenants/12345a7c-c56d-43e8-9549-dd230ce8a038","tenantId":"12345a7c-c56d-43e8-9549-dd230ce8a038"}]}`
|
|
|
|
|
2020-07-31 15:22:34 +02:00
|
|
|
m.On("queryAuthorizationAPI", authorizationURL, "Bearer firstAccessToken").Return([]byte(authBody), 200, nil)
|
2020-05-14 22:54:57 +02:00
|
|
|
data := refreshTokenData("firstRefreshToken")
|
2020-07-31 15:22:34 +02:00
|
|
|
m.On("queryToken", data, "12345a7c-c56d-43e8-9549-dd230ce8a038").Return(azureToken{
|
2020-05-14 22:54:57 +02:00
|
|
|
RefreshToken: "newRefreshToken",
|
|
|
|
AccessToken: "newAccessToken",
|
|
|
|
ExpiresIn: 3600,
|
|
|
|
Foci: "1",
|
|
|
|
}, nil)
|
2020-07-31 15:22:34 +02:00
|
|
|
azureLogin, err := testLoginService(t, m)
|
|
|
|
assert.NilError(t, err)
|
2020-05-14 22:54:57 +02:00
|
|
|
|
2020-07-01 12:25:18 +02:00
|
|
|
err = azureLogin.Login(context.TODO(), "")
|
2020-07-31 15:22:34 +02:00
|
|
|
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, "12345a7c-c56d-43e8-9549-dd230ce8a038")
|
|
|
|
assert.Equal(t, loginToken.Token.Type(), "Bearer")
|
2020-07-01 12:25:18 +02:00
|
|
|
}
|
|
|
|
|
2020-07-31 15:22:34 +02:00
|
|
|
func TestValidLoginRequestedTenant(t *testing.T) {
|
2020-07-01 12:25:18 +02:00
|
|
|
var redirectURL string
|
2020-07-31 15:22:34 +02:00
|
|
|
m := &MockAzureHelper{}
|
|
|
|
m.On("openAzureLoginPage", mock.AnythingOfType("string")).Run(func(args mock.Arguments) {
|
2020-07-01 12:25:18 +02:00
|
|
|
redirectURL = args.Get(0).(string)
|
|
|
|
err := queryKeyValue(redirectURL, "code", "123456879")
|
2020-07-31 15:22:34 +02:00
|
|
|
assert.NilError(t, err)
|
2020-07-01 12:25:18 +02:00
|
|
|
})
|
|
|
|
|
2020-07-31 15:22:34 +02:00
|
|
|
m.On("queryToken", mock.MatchedBy(func(data url.Values) bool {
|
2020-07-01 12:25:18 +02:00
|
|
|
//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},
|
|
|
|
"redirect_uri": []string{redirectURL},
|
|
|
|
})
|
|
|
|
}), "organizations").Return(azureToken{
|
|
|
|
RefreshToken: "firstRefreshToken",
|
|
|
|
AccessToken: "firstAccessToken",
|
|
|
|
ExpiresIn: 3600,
|
|
|
|
Foci: "1",
|
|
|
|
}, nil)
|
|
|
|
|
|
|
|
authBody := `{"value":[{"id":"/tenants/00000000-c56d-43e8-9549-dd230ce8a038","tenantId":"00000000-c56d-43e8-9549-dd230ce8a038"},
|
|
|
|
{"id":"/tenants/12345a7c-c56d-43e8-9549-dd230ce8a038","tenantId":"12345a7c-c56d-43e8-9549-dd230ce8a038"}]}`
|
|
|
|
|
2020-07-31 15:22:34 +02:00
|
|
|
m.On("queryAuthorizationAPI", authorizationURL, "Bearer firstAccessToken").Return([]byte(authBody), 200, nil)
|
2020-07-01 12:25:18 +02:00
|
|
|
data := refreshTokenData("firstRefreshToken")
|
2020-07-31 15:22:34 +02:00
|
|
|
m.On("queryToken", data, "12345a7c-c56d-43e8-9549-dd230ce8a038").Return(azureToken{
|
2020-07-01 12:25:18 +02:00
|
|
|
RefreshToken: "newRefreshToken",
|
|
|
|
AccessToken: "newAccessToken",
|
|
|
|
ExpiresIn: 3600,
|
|
|
|
Foci: "1",
|
|
|
|
}, nil)
|
2020-07-31 15:22:34 +02:00
|
|
|
azureLogin, err := testLoginService(t, m)
|
|
|
|
assert.NilError(t, err)
|
2020-07-01 12:25:18 +02:00
|
|
|
|
|
|
|
err = azureLogin.Login(context.TODO(), "12345a7c-c56d-43e8-9549-dd230ce8a038")
|
2020-07-31 15:22:34 +02:00
|
|
|
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, "12345a7c-c56d-43e8-9549-dd230ce8a038")
|
|
|
|
assert.Equal(t, loginToken.Token.Type(), "Bearer")
|
2020-05-14 22:54:57 +02:00
|
|
|
}
|
|
|
|
|
2020-07-31 15:22:34 +02:00
|
|
|
func TestLoginNoTenant(t *testing.T) {
|
Fix login error when login to an azure account having no associated tenant.
This was encountered by @lorenrh with stack :
```
docker login azure
panic: runtime error: index out of range [0] with length 0
goroutine 1 [running]:
github.com/docker/api/azure/login.AzureLoginService.Login(0xc0004cc2a0, 0x2d, 0xe8b8a0, 0x1476830, 0xe8e4e0, 0xc000498ff0, 0x0, 0x0)
github.com/docker/api/azure/login/login.go:130 +0x1057
github.com/docker/api/azure.(*aciCloudService).Login(0xc0004888c0, 0xe8e4e0, 0xc000498ff0, 0x0, 0xe82720, 0xc0004888c0)
github.com/docker/api/azure/backend.go:283 +0x64
github.com/docker/api/cli/cmd/login.cloudLogin(0xc0004eb600, 0xd422a4, 0x3, 0x1, 0xffffffffffffffff)
github.com/docker/api/cli/cmd/login/login.go:53 +0xae
github.com/docker/api/cli/cmd/login.runLogin(0xc0004eb600, 0xc00049c5f0, 0x1, 0x1, 0x0, 0x0)
github.com/docker/api/cli/cmd/login/login.go:39 +0x15e
github.com/spf13/cobra.(*Command).execute(0xc0004eb600, 0xc00049c5d0, 0x1, 0x1, 0xc0004eb600, 0xc00049c5d0)
github.com/spf13/cobra@v1.0.0/command.go:842 +0x45a
github.com/spf13/cobra.(*Command).ExecuteC(0xc0004a8580, 0xc000498fc0, 0xc41160, 0x1476830)
github.com/spf13/cobra@v1.0.0/command.go:950 +0x350
github.com/spf13/cobra.(*Command).Execute(...)
github.com/spf13/cobra@v1.0.0/command.go:887
github.com/spf13/cobra.(*Command).ExecuteContext(...)
github.com/spf13/cobra@v1.0.0/command.go:880
main.main()
github.com/docker/api/cli/main.go:167 +0x68f
```
2020-06-10 09:36:43 +02:00
|
|
|
var redirectURL string
|
2020-07-31 15:22:34 +02:00
|
|
|
m := &MockAzureHelper{}
|
|
|
|
m.On("openAzureLoginPage", mock.AnythingOfType("string")).Run(func(args mock.Arguments) {
|
Fix login error when login to an azure account having no associated tenant.
This was encountered by @lorenrh with stack :
```
docker login azure
panic: runtime error: index out of range [0] with length 0
goroutine 1 [running]:
github.com/docker/api/azure/login.AzureLoginService.Login(0xc0004cc2a0, 0x2d, 0xe8b8a0, 0x1476830, 0xe8e4e0, 0xc000498ff0, 0x0, 0x0)
github.com/docker/api/azure/login/login.go:130 +0x1057
github.com/docker/api/azure.(*aciCloudService).Login(0xc0004888c0, 0xe8e4e0, 0xc000498ff0, 0x0, 0xe82720, 0xc0004888c0)
github.com/docker/api/azure/backend.go:283 +0x64
github.com/docker/api/cli/cmd/login.cloudLogin(0xc0004eb600, 0xd422a4, 0x3, 0x1, 0xffffffffffffffff)
github.com/docker/api/cli/cmd/login/login.go:53 +0xae
github.com/docker/api/cli/cmd/login.runLogin(0xc0004eb600, 0xc00049c5f0, 0x1, 0x1, 0x0, 0x0)
github.com/docker/api/cli/cmd/login/login.go:39 +0x15e
github.com/spf13/cobra.(*Command).execute(0xc0004eb600, 0xc00049c5d0, 0x1, 0x1, 0xc0004eb600, 0xc00049c5d0)
github.com/spf13/cobra@v1.0.0/command.go:842 +0x45a
github.com/spf13/cobra.(*Command).ExecuteC(0xc0004a8580, 0xc000498fc0, 0xc41160, 0x1476830)
github.com/spf13/cobra@v1.0.0/command.go:950 +0x350
github.com/spf13/cobra.(*Command).Execute(...)
github.com/spf13/cobra@v1.0.0/command.go:887
github.com/spf13/cobra.(*Command).ExecuteContext(...)
github.com/spf13/cobra@v1.0.0/command.go:880
main.main()
github.com/docker/api/cli/main.go:167 +0x68f
```
2020-06-10 09:36:43 +02:00
|
|
|
redirectURL = args.Get(0).(string)
|
|
|
|
err := queryKeyValue(redirectURL, "code", "123456879")
|
2020-07-31 15:22:34 +02:00
|
|
|
assert.NilError(t, err)
|
Fix login error when login to an azure account having no associated tenant.
This was encountered by @lorenrh with stack :
```
docker login azure
panic: runtime error: index out of range [0] with length 0
goroutine 1 [running]:
github.com/docker/api/azure/login.AzureLoginService.Login(0xc0004cc2a0, 0x2d, 0xe8b8a0, 0x1476830, 0xe8e4e0, 0xc000498ff0, 0x0, 0x0)
github.com/docker/api/azure/login/login.go:130 +0x1057
github.com/docker/api/azure.(*aciCloudService).Login(0xc0004888c0, 0xe8e4e0, 0xc000498ff0, 0x0, 0xe82720, 0xc0004888c0)
github.com/docker/api/azure/backend.go:283 +0x64
github.com/docker/api/cli/cmd/login.cloudLogin(0xc0004eb600, 0xd422a4, 0x3, 0x1, 0xffffffffffffffff)
github.com/docker/api/cli/cmd/login/login.go:53 +0xae
github.com/docker/api/cli/cmd/login.runLogin(0xc0004eb600, 0xc00049c5f0, 0x1, 0x1, 0x0, 0x0)
github.com/docker/api/cli/cmd/login/login.go:39 +0x15e
github.com/spf13/cobra.(*Command).execute(0xc0004eb600, 0xc00049c5d0, 0x1, 0x1, 0xc0004eb600, 0xc00049c5d0)
github.com/spf13/cobra@v1.0.0/command.go:842 +0x45a
github.com/spf13/cobra.(*Command).ExecuteC(0xc0004a8580, 0xc000498fc0, 0xc41160, 0x1476830)
github.com/spf13/cobra@v1.0.0/command.go:950 +0x350
github.com/spf13/cobra.(*Command).Execute(...)
github.com/spf13/cobra@v1.0.0/command.go:887
github.com/spf13/cobra.(*Command).ExecuteContext(...)
github.com/spf13/cobra@v1.0.0/command.go:880
main.main()
github.com/docker/api/cli/main.go:167 +0x68f
```
2020-06-10 09:36:43 +02:00
|
|
|
})
|
|
|
|
|
2020-07-31 15:22:34 +02:00
|
|
|
m.On("queryToken", mock.MatchedBy(func(data url.Values) bool {
|
Fix login error when login to an azure account having no associated tenant.
This was encountered by @lorenrh with stack :
```
docker login azure
panic: runtime error: index out of range [0] with length 0
goroutine 1 [running]:
github.com/docker/api/azure/login.AzureLoginService.Login(0xc0004cc2a0, 0x2d, 0xe8b8a0, 0x1476830, 0xe8e4e0, 0xc000498ff0, 0x0, 0x0)
github.com/docker/api/azure/login/login.go:130 +0x1057
github.com/docker/api/azure.(*aciCloudService).Login(0xc0004888c0, 0xe8e4e0, 0xc000498ff0, 0x0, 0xe82720, 0xc0004888c0)
github.com/docker/api/azure/backend.go:283 +0x64
github.com/docker/api/cli/cmd/login.cloudLogin(0xc0004eb600, 0xd422a4, 0x3, 0x1, 0xffffffffffffffff)
github.com/docker/api/cli/cmd/login/login.go:53 +0xae
github.com/docker/api/cli/cmd/login.runLogin(0xc0004eb600, 0xc00049c5f0, 0x1, 0x1, 0x0, 0x0)
github.com/docker/api/cli/cmd/login/login.go:39 +0x15e
github.com/spf13/cobra.(*Command).execute(0xc0004eb600, 0xc00049c5d0, 0x1, 0x1, 0xc0004eb600, 0xc00049c5d0)
github.com/spf13/cobra@v1.0.0/command.go:842 +0x45a
github.com/spf13/cobra.(*Command).ExecuteC(0xc0004a8580, 0xc000498fc0, 0xc41160, 0x1476830)
github.com/spf13/cobra@v1.0.0/command.go:950 +0x350
github.com/spf13/cobra.(*Command).Execute(...)
github.com/spf13/cobra@v1.0.0/command.go:887
github.com/spf13/cobra.(*Command).ExecuteContext(...)
github.com/spf13/cobra@v1.0.0/command.go:880
main.main()
github.com/docker/api/cli/main.go:167 +0x68f
```
2020-06-10 09:36:43 +02:00
|
|
|
//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},
|
|
|
|
"redirect_uri": []string{redirectURL},
|
|
|
|
})
|
|
|
|
}), "organizations").Return(azureToken{
|
|
|
|
RefreshToken: "firstRefreshToken",
|
|
|
|
AccessToken: "firstAccessToken",
|
|
|
|
ExpiresIn: 3600,
|
|
|
|
Foci: "1",
|
|
|
|
}, nil)
|
|
|
|
|
2020-07-01 12:25:18 +02:00
|
|
|
authBody := `{"value":[{"id":"/tenants/12345a7c-c56d-43e8-9549-dd230ce8a038","tenantId":"12345a7c-c56d-43e8-9549-dd230ce8a038"}]}`
|
2020-07-31 15:22:34 +02:00
|
|
|
m.On("queryAuthorizationAPI", authorizationURL, "Bearer firstAccessToken").Return([]byte(authBody), 200, nil)
|
2020-07-01 12:25:18 +02:00
|
|
|
|
2020-07-31 15:22:34 +02:00
|
|
|
azureLogin, err := testLoginService(t, m)
|
|
|
|
assert.NilError(t, err)
|
2020-07-01 12:25:18 +02:00
|
|
|
|
|
|
|
err = azureLogin.Login(context.TODO(), "00000000-c56d-43e8-9549-dd230ce8a038")
|
2020-07-31 15:22:34 +02:00
|
|
|
assert.Error(t, err, "could not find requested azure tenant 00000000-c56d-43e8-9549-dd230ce8a038: login failed")
|
2020-07-01 12:25:18 +02:00
|
|
|
}
|
|
|
|
|
2020-07-31 15:22:34 +02:00
|
|
|
func TestLoginRequestedTenantNotFound(t *testing.T) {
|
2020-07-01 12:25:18 +02:00
|
|
|
var redirectURL string
|
2020-07-31 15:22:34 +02:00
|
|
|
m := &MockAzureHelper{}
|
|
|
|
m.On("openAzureLoginPage", mock.AnythingOfType("string")).Run(func(args mock.Arguments) {
|
2020-07-01 12:25:18 +02:00
|
|
|
redirectURL = args.Get(0).(string)
|
|
|
|
err := queryKeyValue(redirectURL, "code", "123456879")
|
2020-07-31 15:22:34 +02:00
|
|
|
assert.NilError(t, err)
|
2020-07-01 12:25:18 +02:00
|
|
|
})
|
|
|
|
|
2020-07-31 15:22:34 +02:00
|
|
|
m.On("queryToken", mock.MatchedBy(func(data url.Values) bool {
|
2020-07-01 12:25:18 +02:00
|
|
|
//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},
|
|
|
|
"redirect_uri": []string{redirectURL},
|
|
|
|
})
|
|
|
|
}), "organizations").Return(azureToken{
|
|
|
|
RefreshToken: "firstRefreshToken",
|
|
|
|
AccessToken: "firstAccessToken",
|
|
|
|
ExpiresIn: 3600,
|
|
|
|
Foci: "1",
|
|
|
|
}, nil)
|
|
|
|
|
Fix login error when login to an azure account having no associated tenant.
This was encountered by @lorenrh with stack :
```
docker login azure
panic: runtime error: index out of range [0] with length 0
goroutine 1 [running]:
github.com/docker/api/azure/login.AzureLoginService.Login(0xc0004cc2a0, 0x2d, 0xe8b8a0, 0x1476830, 0xe8e4e0, 0xc000498ff0, 0x0, 0x0)
github.com/docker/api/azure/login/login.go:130 +0x1057
github.com/docker/api/azure.(*aciCloudService).Login(0xc0004888c0, 0xe8e4e0, 0xc000498ff0, 0x0, 0xe82720, 0xc0004888c0)
github.com/docker/api/azure/backend.go:283 +0x64
github.com/docker/api/cli/cmd/login.cloudLogin(0xc0004eb600, 0xd422a4, 0x3, 0x1, 0xffffffffffffffff)
github.com/docker/api/cli/cmd/login/login.go:53 +0xae
github.com/docker/api/cli/cmd/login.runLogin(0xc0004eb600, 0xc00049c5f0, 0x1, 0x1, 0x0, 0x0)
github.com/docker/api/cli/cmd/login/login.go:39 +0x15e
github.com/spf13/cobra.(*Command).execute(0xc0004eb600, 0xc00049c5d0, 0x1, 0x1, 0xc0004eb600, 0xc00049c5d0)
github.com/spf13/cobra@v1.0.0/command.go:842 +0x45a
github.com/spf13/cobra.(*Command).ExecuteC(0xc0004a8580, 0xc000498fc0, 0xc41160, 0x1476830)
github.com/spf13/cobra@v1.0.0/command.go:950 +0x350
github.com/spf13/cobra.(*Command).Execute(...)
github.com/spf13/cobra@v1.0.0/command.go:887
github.com/spf13/cobra.(*Command).ExecuteContext(...)
github.com/spf13/cobra@v1.0.0/command.go:880
main.main()
github.com/docker/api/cli/main.go:167 +0x68f
```
2020-06-10 09:36:43 +02:00
|
|
|
authBody := `{"value":[]}`
|
2020-07-31 15:22:34 +02:00
|
|
|
m.On("queryAuthorizationAPI", authorizationURL, "Bearer firstAccessToken").Return([]byte(authBody), 200, nil)
|
Fix login error when login to an azure account having no associated tenant.
This was encountered by @lorenrh with stack :
```
docker login azure
panic: runtime error: index out of range [0] with length 0
goroutine 1 [running]:
github.com/docker/api/azure/login.AzureLoginService.Login(0xc0004cc2a0, 0x2d, 0xe8b8a0, 0x1476830, 0xe8e4e0, 0xc000498ff0, 0x0, 0x0)
github.com/docker/api/azure/login/login.go:130 +0x1057
github.com/docker/api/azure.(*aciCloudService).Login(0xc0004888c0, 0xe8e4e0, 0xc000498ff0, 0x0, 0xe82720, 0xc0004888c0)
github.com/docker/api/azure/backend.go:283 +0x64
github.com/docker/api/cli/cmd/login.cloudLogin(0xc0004eb600, 0xd422a4, 0x3, 0x1, 0xffffffffffffffff)
github.com/docker/api/cli/cmd/login/login.go:53 +0xae
github.com/docker/api/cli/cmd/login.runLogin(0xc0004eb600, 0xc00049c5f0, 0x1, 0x1, 0x0, 0x0)
github.com/docker/api/cli/cmd/login/login.go:39 +0x15e
github.com/spf13/cobra.(*Command).execute(0xc0004eb600, 0xc00049c5d0, 0x1, 0x1, 0xc0004eb600, 0xc00049c5d0)
github.com/spf13/cobra@v1.0.0/command.go:842 +0x45a
github.com/spf13/cobra.(*Command).ExecuteC(0xc0004a8580, 0xc000498fc0, 0xc41160, 0x1476830)
github.com/spf13/cobra@v1.0.0/command.go:950 +0x350
github.com/spf13/cobra.(*Command).Execute(...)
github.com/spf13/cobra@v1.0.0/command.go:887
github.com/spf13/cobra.(*Command).ExecuteContext(...)
github.com/spf13/cobra@v1.0.0/command.go:880
main.main()
github.com/docker/api/cli/main.go:167 +0x68f
```
2020-06-10 09:36:43 +02:00
|
|
|
|
2020-07-31 15:22:34 +02:00
|
|
|
azureLogin, err := testLoginService(t, m)
|
|
|
|
assert.NilError(t, err)
|
Fix login error when login to an azure account having no associated tenant.
This was encountered by @lorenrh with stack :
```
docker login azure
panic: runtime error: index out of range [0] with length 0
goroutine 1 [running]:
github.com/docker/api/azure/login.AzureLoginService.Login(0xc0004cc2a0, 0x2d, 0xe8b8a0, 0x1476830, 0xe8e4e0, 0xc000498ff0, 0x0, 0x0)
github.com/docker/api/azure/login/login.go:130 +0x1057
github.com/docker/api/azure.(*aciCloudService).Login(0xc0004888c0, 0xe8e4e0, 0xc000498ff0, 0x0, 0xe82720, 0xc0004888c0)
github.com/docker/api/azure/backend.go:283 +0x64
github.com/docker/api/cli/cmd/login.cloudLogin(0xc0004eb600, 0xd422a4, 0x3, 0x1, 0xffffffffffffffff)
github.com/docker/api/cli/cmd/login/login.go:53 +0xae
github.com/docker/api/cli/cmd/login.runLogin(0xc0004eb600, 0xc00049c5f0, 0x1, 0x1, 0x0, 0x0)
github.com/docker/api/cli/cmd/login/login.go:39 +0x15e
github.com/spf13/cobra.(*Command).execute(0xc0004eb600, 0xc00049c5d0, 0x1, 0x1, 0xc0004eb600, 0xc00049c5d0)
github.com/spf13/cobra@v1.0.0/command.go:842 +0x45a
github.com/spf13/cobra.(*Command).ExecuteC(0xc0004a8580, 0xc000498fc0, 0xc41160, 0x1476830)
github.com/spf13/cobra@v1.0.0/command.go:950 +0x350
github.com/spf13/cobra.(*Command).Execute(...)
github.com/spf13/cobra@v1.0.0/command.go:887
github.com/spf13/cobra.(*Command).ExecuteContext(...)
github.com/spf13/cobra@v1.0.0/command.go:880
main.main()
github.com/docker/api/cli/main.go:167 +0x68f
```
2020-06-10 09:36:43 +02:00
|
|
|
|
2020-07-01 12:25:18 +02:00
|
|
|
err = azureLogin.Login(context.TODO(), "")
|
2020-07-31 15:22:34 +02:00
|
|
|
assert.Error(t, err, "could not find azure tenant: login failed")
|
Fix login error when login to an azure account having no associated tenant.
This was encountered by @lorenrh with stack :
```
docker login azure
panic: runtime error: index out of range [0] with length 0
goroutine 1 [running]:
github.com/docker/api/azure/login.AzureLoginService.Login(0xc0004cc2a0, 0x2d, 0xe8b8a0, 0x1476830, 0xe8e4e0, 0xc000498ff0, 0x0, 0x0)
github.com/docker/api/azure/login/login.go:130 +0x1057
github.com/docker/api/azure.(*aciCloudService).Login(0xc0004888c0, 0xe8e4e0, 0xc000498ff0, 0x0, 0xe82720, 0xc0004888c0)
github.com/docker/api/azure/backend.go:283 +0x64
github.com/docker/api/cli/cmd/login.cloudLogin(0xc0004eb600, 0xd422a4, 0x3, 0x1, 0xffffffffffffffff)
github.com/docker/api/cli/cmd/login/login.go:53 +0xae
github.com/docker/api/cli/cmd/login.runLogin(0xc0004eb600, 0xc00049c5f0, 0x1, 0x1, 0x0, 0x0)
github.com/docker/api/cli/cmd/login/login.go:39 +0x15e
github.com/spf13/cobra.(*Command).execute(0xc0004eb600, 0xc00049c5d0, 0x1, 0x1, 0xc0004eb600, 0xc00049c5d0)
github.com/spf13/cobra@v1.0.0/command.go:842 +0x45a
github.com/spf13/cobra.(*Command).ExecuteC(0xc0004a8580, 0xc000498fc0, 0xc41160, 0x1476830)
github.com/spf13/cobra@v1.0.0/command.go:950 +0x350
github.com/spf13/cobra.(*Command).Execute(...)
github.com/spf13/cobra@v1.0.0/command.go:887
github.com/spf13/cobra.(*Command).ExecuteContext(...)
github.com/spf13/cobra@v1.0.0/command.go:880
main.main()
github.com/docker/api/cli/main.go:167 +0x68f
```
2020-06-10 09:36:43 +02:00
|
|
|
}
|
|
|
|
|
2020-07-31 15:22:34 +02:00
|
|
|
func TestLoginAuthorizationFailed(t *testing.T) {
|
2020-05-14 22:54:57 +02:00
|
|
|
var redirectURL string
|
2020-07-31 15:22:34 +02:00
|
|
|
m := &MockAzureHelper{}
|
|
|
|
m.On("openAzureLoginPage", mock.AnythingOfType("string")).Run(func(args mock.Arguments) {
|
2020-05-14 22:54:57 +02:00
|
|
|
redirectURL = args.Get(0).(string)
|
|
|
|
err := queryKeyValue(redirectURL, "code", "123456879")
|
2020-07-31 15:22:34 +02:00
|
|
|
assert.NilError(t, err)
|
2020-05-14 22:54:57 +02:00
|
|
|
})
|
|
|
|
|
2020-07-31 15:22:34 +02:00
|
|
|
m.On("queryToken", mock.MatchedBy(func(data url.Values) bool {
|
2020-05-14 22:54:57 +02:00
|
|
|
//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},
|
|
|
|
"redirect_uri": []string{redirectURL},
|
|
|
|
})
|
|
|
|
}), "organizations").Return(azureToken{
|
|
|
|
RefreshToken: "firstRefreshToken",
|
|
|
|
AccessToken: "firstAccessToken",
|
|
|
|
ExpiresIn: 3600,
|
|
|
|
Foci: "1",
|
|
|
|
}, nil)
|
|
|
|
|
|
|
|
authBody := `[access denied]`
|
|
|
|
|
2020-07-31 15:22:34 +02:00
|
|
|
m.On("queryAuthorizationAPI", authorizationURL, "Bearer firstAccessToken").Return([]byte(authBody), 400, nil)
|
2020-05-14 22:54:57 +02:00
|
|
|
|
2020-07-31 15:22:34 +02:00
|
|
|
azureLogin, err := testLoginService(t, m)
|
|
|
|
assert.NilError(t, err)
|
2020-05-14 22:54:57 +02:00
|
|
|
|
2020-07-01 12:25:18 +02:00
|
|
|
err = azureLogin.Login(context.TODO(), "")
|
2020-07-31 15:22:34 +02:00
|
|
|
assert.Error(t, err, "unable to login status code 400: [access denied]: login failed")
|
2020-05-14 22:54:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func refreshTokenData(refreshToken string) url.Values {
|
|
|
|
return url.Values{
|
|
|
|
"grant_type": []string{"refresh_token"},
|
|
|
|
"client_id": []string{clientID},
|
|
|
|
"scope": []string{scopes},
|
|
|
|
"refresh_token": []string{refreshToken},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func queryKeyValue(redirectURL string, key string, value string) error {
|
|
|
|
req, err := http.NewRequest("GET", redirectURL, nil)
|
2020-07-31 15:22:34 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-05-14 22:54:57 +02:00
|
|
|
q := req.URL.Query()
|
|
|
|
q.Add(key, value)
|
|
|
|
req.URL.RawQuery = q.Encode()
|
|
|
|
client := &http.Client{}
|
|
|
|
_, err = client.Do(req)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-05-12 17:26:11 +02:00
|
|
|
type MockAzureHelper struct {
|
|
|
|
mock.Mock
|
|
|
|
}
|
|
|
|
|
2020-05-14 22:54:57 +02:00
|
|
|
func (s *MockAzureHelper) queryToken(data url.Values, tenantID string) (token azureToken, err error) {
|
2020-05-12 17:26:11 +02:00
|
|
|
args := s.Called(data, tenantID)
|
|
|
|
return args.Get(0).(azureToken), args.Error(1)
|
|
|
|
}
|
2020-05-14 22:54:57 +02:00
|
|
|
|
|
|
|
func (s *MockAzureHelper) queryAuthorizationAPI(authorizationURL string, authorizationHeader string) ([]byte, int, error) {
|
|
|
|
args := s.Called(authorizationURL, authorizationHeader)
|
|
|
|
return args.Get(0).([]byte), args.Int(1), args.Error(2)
|
|
|
|
}
|
|
|
|
|
2020-07-01 16:47:49 +02:00
|
|
|
func (s *MockAzureHelper) openAzureLoginPage(redirectURL string) error {
|
2020-05-14 22:54:57 +02:00
|
|
|
s.Called(redirectURL)
|
2020-07-01 16:47:49 +02:00
|
|
|
return nil
|
2020-05-14 22:54:57 +02:00
|
|
|
}
|