Implement azure logout

This commit is contained in:
Guillaume Tardif 2020-07-08 16:01:54 +02:00
parent 78cb0a87c7
commit 492ce96dd4
10 changed files with 130 additions and 23 deletions

View File

@ -342,6 +342,10 @@ func (cs *aciCloudService) Login(ctx context.Context, params map[string]string)
return cs.loginService.Login(ctx, params[login.TenantIDLoginParam])
}
func (cs *aciCloudService) Logout(ctx context.Context) error {
return cs.loginService.Logout(ctx)
}
func (cs *aciCloudService) CreateContextData(ctx context.Context, params map[string]string) (interface{}, string, error) {
contextHelper := newContextCreateHelper()
return contextHelper.createContextData(ctx, params)

View File

@ -22,14 +22,13 @@ import (
"fmt"
"net/http"
"net/url"
"path/filepath"
"os"
"strconv"
"time"
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/adal"
auth2 "github.com/Azure/go-autorest/autorest/azure/auth"
"github.com/Azure/go-autorest/autorest/azure/cli"
"github.com/Azure/go-autorest/autorest/date"
"github.com/pkg/errors"
"golang.org/x/oauth2"
@ -80,7 +79,7 @@ const tokenStoreFilename = "dockerAccessToken.json"
// NewAzureLoginService creates a NewAzureLoginService
func NewAzureLoginService() (AzureLoginService, error) {
return newAzureLoginServiceFromPath(getTokenStorePath(), azureAPIHelper{})
return newAzureLoginServiceFromPath(GetTokenStorePath(), azureAPIHelper{})
}
func newAzureLoginServiceFromPath(tokenStorePath string, helper apiHelper) (AzureLoginService, error) {
@ -120,6 +119,15 @@ func (login AzureLoginService) TestLoginFromServicePrincipal(clientID string, cl
return nil
}
// Logout remove azure token data
func (login AzureLoginService) Logout(ctx context.Context) error {
err := login.tokenStore.removeData()
if os.IsNotExist(err) {
return errors.New("No Azure login data to be removed")
}
return err
}
// Login performs an Azure login through a web browser
func (login AzureLoginService) Login(ctx context.Context, requestedTenantID string) error {
queryCh := make(chan localResponse, 1)
@ -208,11 +216,6 @@ func getTenantID(tenantValues []tenantValue, requestedTenantID string) (string,
return "", errors.Errorf("could not find requested azure tenant %s", requestedTenantID)
}
func getTokenStorePath() string {
cliPath, _ := cli.AccessTokensPath()
return filepath.Join(filepath.Dir(cliPath), tokenStoreFilename)
}
func toOAuthToken(token azureToken) oauth2.Token {
expireTime := time.Now().Add(time.Duration(token.ExpiresIn) * time.Second)
oauthToken := oauth2.Token{
@ -241,7 +244,7 @@ func spToOAuthToken(token adal.Token) (oauth2.Token, error) {
// NewAuthorizerFromLogin creates an authorizer based on login access token
func NewAuthorizerFromLogin() (autorest.Authorizer, error) {
return newAuthorizerFromLoginStorePath(getTokenStorePath())
return newAuthorizerFromLoginStorePath(GetTokenStorePath())
}
func newAuthorizerFromLoginStorePath(storeTokenPath string) (autorest.Authorizer, error) {

View File

@ -23,6 +23,8 @@ import (
"os"
"path/filepath"
"github.com/Azure/go-autorest/autorest/azure/cli"
"golang.org/x/oauth2"
)
@ -57,6 +59,12 @@ func newTokenStore(path string) (tokenStore, error) {
}, nil
}
// GetTokenStorePath the path for token store
func GetTokenStorePath() string {
cliPath, _ := cli.AccessTokensPath()
return filepath.Join(filepath.Dir(cliPath), tokenStoreFilename)
}
func (store tokenStore) writeLoginInfo(info TokenInfo) error {
bytes, err := json.MarshalIndent(info, "", " ")
if err != nil {
@ -76,3 +84,7 @@ func (store tokenStore) readToken() (TokenInfo, error) {
}
return loginInfo, nil
}
func (store tokenStore) removeData() error {
return os.Remove(store.filePath)
}

View File

@ -35,7 +35,7 @@ import (
func Command() *cobra.Command {
cmd := &cobra.Command{
Use: "login [OPTIONS] [SERVER]",
Short: "Log in to a Docker registry",
Short: "Log in to a Docker registry or cloud backend",
Long: "Log in to a Docker registry or cloud backend.\nIf no registry server is specified, the default is defined by the daemon.",
Args: cobra.MaximumNArgs(1),
RunE: runLogin,

View File

@ -0,0 +1,42 @@
package logout
import (
"context"
"fmt"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/docker/api/client"
"github.com/docker/api/errdefs"
)
// AzureLogoutCommand returns the azure logout command
func AzureLogoutCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "azure",
Short: "Logout from Azure",
Args: cobra.MaximumNArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
return cloudLogout(cmd, "aci")
},
}
return cmd
}
func cloudLogout(cmd *cobra.Command, backendType string) error {
ctx := cmd.Context()
cs, err := client.GetCloudService(ctx, backendType)
if err != nil {
return errors.Wrap(errdefs.ErrLoginFailed, "cannot connect to backend")
}
err = cs.Logout(ctx)
if errors.Is(err, context.Canceled) {
return errors.New("logout canceled")
}
if err != nil {
return err
}
fmt.Println("Removing login credentials for Azure")
return nil
}

25
cli/cmd/logout/logout.go Normal file
View File

@ -0,0 +1,25 @@
package logout
import (
"github.com/spf13/cobra"
"github.com/docker/api/cli/mobycli"
)
// Command returns the login command
func Command() *cobra.Command {
cmd := &cobra.Command{
Use: "logout [SERVER]",
Short: "Log out from a Docker registry or cloud backend",
Long: "Log out from a Docker registry or cloud backend.\nIf no server is specified, the default is defined by the daemon.",
Args: cobra.MaximumNArgs(0),
RunE: runLogout,
}
cmd.AddCommand(AzureLogoutCommand())
return cmd
}
func runLogout(cmd *cobra.Command, args []string) error {
return mobycli.ExecCmd(cmd)
}

View File

@ -27,6 +27,8 @@ import (
"syscall"
"time"
"github.com/docker/api/cli/cmd/logout"
"github.com/docker/api/errdefs"
"github.com/pkg/errors"
@ -59,6 +61,7 @@ var (
ownCommands = map[string]struct{}{
"context": {},
"login": {},
"logout": {},
"serve": {},
"version": {},
}
@ -117,6 +120,7 @@ func main() {
cmd.InspectCommand(),
compose.Command(),
login.Command(),
logout.Command(),
cmd.VersionCommand(version),
)

View File

@ -26,7 +26,9 @@ import (
type Service interface {
// Login login to cloud provider
Login(ctx context.Context, params map[string]string) error
// Login login to cloud provider
// Logout logout from cloud provider
Logout(ctx context.Context) error
// CreateContextData create data for cloud context
CreateContextData(ctx context.Context, params map[string]string) (contextData interface{}, description string, err error)
}
@ -38,6 +40,11 @@ func NotImplementedCloudService() (Service, error) {
type notImplementedCloudService struct {
}
// Logout login to cloud provider
func (cs notImplementedCloudService) Logout(ctx context.Context) error {
return errdefs.ErrNotImplemented
}
func (cs notImplementedCloudService) Login(ctx context.Context, params map[string]string) error {
return errdefs.ErrNotImplemented
}

View File

@ -22,10 +22,13 @@ import (
"math/rand"
"net/url"
"os"
"os/exec"
"strings"
"testing"
"time"
"github.com/docker/api/errdefs"
"github.com/Azure/azure-sdk-for-go/profiles/2019-03-01/resources/mgmt/resources"
azure_storage "github.com/Azure/azure-sdk-for-go/profiles/2019-03-01/storage/mgmt/storage"
"github.com/Azure/azure-storage-file-go/azfile"
@ -58,6 +61,25 @@ type E2eACISuite struct {
Suite
}
func (s *E2eACISuite) TestLoginLogoutCreateContextError() {
s.Step("Logs in azure using service principal credentials", azureLogin)
s.Step("logout from azure", func() {
output := s.NewDockerCommand("logout", "azure").ExecOrDie()
Expect(output).To(ContainSubstring(""))
_, err := os.Stat(login.GetTokenStorePath())
Expect(os.IsNotExist(err)).To(BeTrue())
})
s.Step("check context create fails with an explicit error and returns a specific error code", func() {
cmd := exec.Command("docker", "context", "create", "aci", "someContext")
bytes, err := cmd.CombinedOutput()
Expect(err).NotTo(BeNil())
Expect(string(bytes)).To(ContainSubstring("not logged in to azure, you need to run \"docker login azure\" first"))
Expect(cmd.ProcessState.ExitCode()).To(Equal(errdefs.ExitCodeLoginRequired))
})
}
func (s *E2eACISuite) TestACIRunSingleContainer() {
resourceGroupName := s.setupTestResourceGroup()
defer deleteResourceGroup(resourceGroupName)

View File

@ -18,17 +18,13 @@ package main
import (
"os"
"os/exec"
"path/filepath"
"runtime"
"testing"
"time"
"github.com/docker/api/errdefs"
. "github.com/onsi/gomega"
"github.com/stretchr/testify/suite"
"gotest.tools/golden"
. "github.com/docker/api/tests/framework"
@ -46,14 +42,6 @@ func (s *E2eSuite) TestContextHelp() {
Expect(output).To(ContainSubstring("--resource-group"))
}
func (s *E2eSuite) TestContextCreateAciExitWithErrorCodeIfLoginRequired() {
cmd := exec.Command("docker", "context", "create", "aci", "someContext")
output, err := cmd.CombinedOutput()
Expect(err).NotTo(BeNil())
Expect(string(output)).To(ContainSubstring("not logged in to azure, you need to run \"docker login azure\" first"))
Expect(cmd.ProcessState.ExitCode()).To(Equal(errdefs.ExitCodeLoginRequired))
}
func (s *E2eSuite) TestListAndShowDefaultContext() {
output := s.NewDockerCommand("context", "show").ExecOrDie()
Expect(output).To(ContainSubstring("default"))