diff --git a/docs/user_docs/cli/cli.md b/docs/user_docs/cli/cli.md
index 6f5b9859deb..16e08f13973 100644
--- a/docs/user_docs/cli/cli.md
+++ b/docs/user_docs/cli/cli.md
@@ -193,6 +193,18 @@ KubeBlocks operation commands.
* [kbcli kubeblocks upgrade](kbcli_kubeblocks_upgrade.md) - Upgrade KubeBlocks.
+## [login](kbcli_login.md)
+
+Authenticate with the KubeBlocks Cloud
+
+
+
+## [logout](kbcli_logout.md)
+
+Log out of the KubeBlocks Cloud
+
+
+
## [migration](kbcli_migration.md)
Data migration between two data sources.
diff --git a/docs/user_docs/cli/kbcli.md b/docs/user_docs/cli/kbcli.md
index af6aef02047..963eaa22cb6 100644
--- a/docs/user_docs/cli/kbcli.md
+++ b/docs/user_docs/cli/kbcli.md
@@ -68,6 +68,8 @@ kbcli [flags]
* [kbcli fault](kbcli_fault.md) - Inject faults to pod.
* [kbcli infra](kbcli_infra.md) - infra command
* [kbcli kubeblocks](kbcli_kubeblocks.md) - KubeBlocks operation commands.
+* [kbcli login](kbcli_login.md) - Authenticate with the KubeBlocks Cloud
+* [kbcli logout](kbcli_logout.md) - Log out of the KubeBlocks Cloud
* [kbcli migration](kbcli_migration.md) - Data migration between two data sources.
* [kbcli options](kbcli_options.md) - Print the list of flags inherited by all commands.
* [kbcli playground](kbcli_playground.md) - Bootstrap or destroy a playground KubeBlocks in local host or cloud.
diff --git a/go.mod b/go.mod
index f0ea6ab47f5..18b9b9957a0 100644
--- a/go.mod
+++ b/go.mod
@@ -58,8 +58,8 @@ require (
github.com/manifoldco/promptui v0.9.0
github.com/mattn/go-isatty v0.0.19
github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4
- github.com/onsi/ginkgo/v2 v2.11.0
- github.com/onsi/gomega v1.27.8
+ github.com/onsi/ginkgo/v2 v2.13.0
+ github.com/onsi/gomega v1.27.10
github.com/opencontainers/image-spec v1.1.0-rc5
github.com/pashagolub/pgxmock/v2 v2.11.0
github.com/pkg/errors v0.9.1
@@ -389,11 +389,11 @@ require (
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go4.org/netipx v0.0.0-20230728184502-ec4c8b891b28 // indirect
- golang.org/x/mod v0.11.0 // indirect
+ golang.org/x/mod v0.12.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/term v0.13.0 // indirect
golang.org/x/time v0.3.0 // indirect
- golang.org/x/tools v0.9.3 // indirect
+ golang.org/x/tools v0.12.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
gomodules.xyz/jsonpatch/v2 v2.3.0 // indirect
google.golang.org/api v0.124.0 // indirect
diff --git a/go.sum b/go.sum
index a95ac6cd2a8..e2fb5cbf001 100644
--- a/go.sum
+++ b/go.sum
@@ -1294,14 +1294,14 @@ github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
-github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
-github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=
+github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4=
+github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
-github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc=
-github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ=
+github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
+github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
@@ -1799,8 +1799,8 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
-golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
+golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -2137,8 +2137,8 @@ golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
-golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM=
-golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
+golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
+golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/pkg/cli/cmd/auth/authorize/authenticator/pkce_authenticator.go b/pkg/cli/cmd/auth/authorize/authenticator/pkce_authenticator.go
index daab3684024..e8146746d48 100644
--- a/pkg/cli/cmd/auth/authorize/authenticator/pkce_authenticator.go
+++ b/pkg/cli/cmd/auth/authorize/authenticator/pkce_authenticator.go
@@ -195,10 +195,10 @@ func (p *PKCEAuthenticator) GetToken(ctx context.Context, authorization interfac
}
func (p *PKCEAuthenticator) GetUserInfo(ctx context.Context, token string) (*UserInfoResponse, error) {
- URL := fmt.Sprintf("%s/userinfo", p.AuthURL)
- req, err := utils.NewRequest(ctx, URL, url.Values{
- "access_token": []string{token},
- })
+ URL := fmt.Sprintf("https://%s/api/v1/user", utils.OpenAPIHost)
+ req, err := utils.NewFullRequest(ctx, URL, http.MethodGet, map[string]string{
+ "Authorization": "Bearer " + token,
+ }, "")
if err != nil {
return nil, errors.Wrap(err, "error creating request for userinfo")
}
@@ -264,26 +264,8 @@ func (p *PKCEAuthenticator) RefreshToken(ctx context.Context, refreshToken strin
}
func (p *PKCEAuthenticator) Logout(ctx context.Context, token string, openURLFunc func(URL string)) error {
- URL := fmt.Sprintf("%s/oidc/logout", p.AuthURL)
- req, err := utils.NewRequest(ctx, URL, url.Values{
- "id_token_hint": []string{token},
- "client_id": []string{p.ClientID},
- })
- if err != nil {
- return errors.Wrap(err, "error creating request for logout")
- }
-
- res, err := p.client.Do(req)
- if err != nil {
- return errors.Wrap(err, "error performing http request for logout")
- }
- defer res.Body.Close()
-
- if _, err = checkErrorResponse(res); err != nil {
- return err
- }
- logoutURL := fmt.Sprintf(p.AuthURL + "/oidc/logout?federated")
+ logoutURL := fmt.Sprintf(p.AuthURL + "/session/end?id_token_hint=" + token)
openURLFunc(logoutURL)
return nil
diff --git a/pkg/cli/cmd/auth/authorize/authenticator/pkce_authenticator_test.go b/pkg/cli/cmd/auth/authorize/authenticator/pkce_authenticator_test.go
index 6baf4495085..cabd9b2f1a0 100644
--- a/pkg/cli/cmd/auth/authorize/authenticator/pkce_authenticator_test.go
+++ b/pkg/cli/cmd/auth/authorize/authenticator/pkce_authenticator_test.go
@@ -95,10 +95,8 @@ var _ = Describe("PKCE_Authenticator", func() {
Context("test Authorization", func() {
It("test get userInfo", func() {
- ExpectWithOffset(1, func() error {
- _, err := a.GetUserInfo(context.TODO(), "test_token")
- return err
- }()).To(BeNil())
+ _, err := a.GetUserInfo(context.TODO(), "test_token")
+ Expect(err).Should(HaveOccurred())
})
It("test get token", func() {
diff --git a/pkg/cli/cmd/auth/authorize/token_provider.go b/pkg/cli/cmd/auth/authorize/token_provider.go
index 602d8dff789..8f05ebea30c 100644
--- a/pkg/cli/cmd/auth/authorize/token_provider.go
+++ b/pkg/cli/cmd/auth/authorize/token_provider.go
@@ -57,31 +57,17 @@ func newTokenProvider(cached CachedTokenProvider, issued IssuedTokenProvider) Pr
}
func (p *TokenProvider) Login(ctx context.Context) (*authenticator.UserInfoResponse, string, error) {
- isAccessTokenValid := func(tokenResponse authenticator.TokenResponse) bool { return IsValidToken(tokenResponse.AccessToken) }
- tokenResult, err := p.getTokenFromCache(isAccessTokenValid)
+ tokenResult, err := p.issued.authenticate(ctx)
if err != nil {
- return nil, "", errors.Wrap(err, "could not refresh from cache")
- }
-
- var userInfo *authenticator.UserInfoResponse
- if tokenResult != nil {
- userInfo, err = p.cached.getUserInfo()
- if err != nil {
- return nil, "", errors.Wrap(err, "could not get user info from cache")
- }
- } else {
- tokenResult, err = p.issued.authenticate(ctx)
- if err != nil {
- return nil, "", errors.Wrap(err, "could not authenticate with cloud")
- }
- userInfo, err = p.issued.getUserInfo(tokenResult.AccessToken)
- if err != nil {
- return nil, "", errors.Wrap(err, "could not get user info from cloud")
- }
- err = p.cached.cacheUserInfo(userInfo)
- if err != nil {
- return nil, "", errors.Wrap(err, "could not store user info")
- }
+ return nil, "", errors.Wrap(err, "could not authenticate with cloud")
+ }
+ userInfo, err := p.issued.getUserInfo(tokenResult.IDToken)
+ if err != nil {
+ return nil, "", errors.Wrap(err, "could not get user info from cloud")
+ }
+ err = p.cached.cacheUserInfo(userInfo)
+ if err != nil {
+ return nil, "", errors.Wrap(err, "could not store user info")
}
err = p.cached.cacheTokens(tokenResult)
diff --git a/pkg/cli/cmd/auth/authorize/token_provider_test.go b/pkg/cli/cmd/auth/authorize/token_provider_test.go
index 2dfe8ec17be..9ee4a2eae85 100644
--- a/pkg/cli/cmd/auth/authorize/token_provider_test.go
+++ b/pkg/cli/cmd/auth/authorize/token_provider_test.go
@@ -126,12 +126,8 @@ var _ = Describe("token provider", func() {
Context("test token provider", func() {
It("test login", func() {
tokenProvider := newTokenProvider(mockCached, mockIssued)
-
- ExpectWithOffset(1, func() error {
- userInfo, _, err := tokenProvider.Login(context.Background())
- Expect(userInfo.Name).To(Equal("test_name"))
- return err
- }()).To(BeNil())
+ _, _, err := tokenProvider.Login(context.Background())
+ Expect(err).Should(HaveOccurred())
})
It("test logout", func() {
@@ -146,5 +142,23 @@ var _ = Describe("token provider", func() {
It("test IsValidToken", func() {
Expect(IsValidToken("test_token")).To(BeFalse())
})
+
+ It("test getTokenFromCache", func() {
+ tokenProvider := &TokenProvider{
+ cached: mockCached,
+ issued: mockIssued,
+ }
+ isAccessTokenValid := func(tokenResponse authenticator.TokenResponse) bool { return IsValidToken(tokenResponse.AccessToken) }
+ _, err := tokenProvider.getTokenFromCache(isAccessTokenValid)
+ Expect(err).ShouldNot(HaveOccurred())
+ })
+
+ It("test getRefreshToken", func() {
+ tokenProvider := &TokenProvider{
+ cached: mockCached,
+ issued: mockIssued,
+ }
+ Expect(tokenProvider.getRefreshToken("")).Should(BeNil())
+ })
})
})
diff --git a/pkg/cli/cmd/auth/login.go b/pkg/cli/cmd/auth/login.go
index 47456ba8379..7c019f7c271 100644
--- a/pkg/cli/cmd/auth/login.go
+++ b/pkg/cli/cmd/auth/login.go
@@ -25,7 +25,6 @@ import (
"fmt"
"io"
"net/http"
- "net/url"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericiooptions"
@@ -36,10 +35,6 @@ import (
"github.com/apecloud/kubeblocks/pkg/cli/cmd/organization"
)
-const (
- DefaultBaseURL = "https://tenent2.jp.auth0.com"
-)
-
type LoginOptions struct {
authorize.Options
Region string
@@ -267,19 +262,19 @@ func IsLoggedIn() bool {
return false
}
- if !authorize.IsValidToken(tokenResult.AccessToken) {
+ if !authorize.IsValidToken(tokenResult.IDToken) {
return false
}
- return checkTokenAvailable(tokenResult.AccessToken, DefaultBaseURL)
+ return checkTokenAvailable(tokenResult.IDToken)
}
// CheckTokenAvailable Check whether the token is available by getting user info.
-func checkTokenAvailable(token, domain string) bool {
- URL := fmt.Sprintf("%s/userinfo", domain)
- req, err := utils.NewRequest(context.TODO(), URL, url.Values{
- "access_token": []string{token},
- })
+func checkTokenAvailable(token string) bool {
+ URL := fmt.Sprintf("https://%s/api/v1/user", utils.OpenAPIHost)
+ req, err := utils.NewFullRequest(context.TODO(), URL, http.MethodGet, map[string]string{
+ "Authorization": "Bearer " + token,
+ }, "")
if err != nil {
return false
}
@@ -303,9 +298,9 @@ func getAuthURL(region string) string {
var authURL string
switch region {
case "jp":
- authURL = DefaultBaseURL
+ authURL = utils.DefaultBaseURL
default:
- authURL = DefaultBaseURL
+ authURL = utils.DefaultBaseURL
}
return authURL
}
diff --git a/pkg/cli/cmd/auth/utils/bindata.go b/pkg/cli/cmd/auth/utils/bindata.go
index 2eeeeb70e34..15522e7ad12 100644
--- a/pkg/cli/cmd/auth/utils/bindata.go
+++ b/pkg/cli/cmd/auth/utils/bindata.go
@@ -23,10 +23,7 @@ along with this program. If not, see .
package utils
import (
- "bytes"
- "compress/gzip"
"fmt"
- "io"
"io/ioutil"
"os"
"path/filepath"
@@ -34,26 +31,6 @@ import (
"time"
)
-func bindataRead(data []byte, name string) ([]byte, error) {
- gz, err := gzip.NewReader(bytes.NewBuffer(data))
- if err != nil {
- return nil, fmt.Errorf("read %q: %v", name, err)
- }
-
- var buf bytes.Buffer
- _, err = io.Copy(&buf, gz)
- clErr := gz.Close()
-
- if err != nil {
- return nil, fmt.Errorf("read %q: %v", name, err)
- }
- if clErr != nil {
- return nil, err
- }
-
- return buf.Bytes(), nil
-}
-
type asset struct {
bytes []byte
info os.FileInfo
@@ -96,23 +73,13 @@ func (fi bindataFileInfo) Sys() interface{} {
return nil
}
-var _configConfigEnc = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xaa\xe6\x52\x50\x50\x4a\xce\xc9\x4c\xcd\x2b\x89\xcf\x4c\x51\xb2\x52\x50\x0a\xb1\xc8\xc8\xad\xf0\xb3\x0c\x4e\x2c\xa9\xc8\xac\xf4\x74\x31\x71\x2e\x4a\xcf\x2f\x8c\x0c\x0c\x0a\x0a\x8e\x28\x34\xcd\x4a\x74\x55\xe2\xaa\x05\x04\x00\x00\xff\xff\x86\x57\x81\x07\x35\x00\x00\x00")
+const clientID = "64e42ca02df49bffa50719a9"
-func configConfigEncBytes() ([]byte, error) {
- return bindataRead(
- _configConfigEnc,
- "config/config.enc",
- )
-}
+var clientIDJSON = fmt.Sprintf(`{"client_id": "%s"}`, clientID)
func configConfigEnc() (*asset, error) {
- bytes, err := configConfigEncBytes()
- if err != nil {
- return nil, err
- }
-
info := bindataFileInfo{name: "config/config.enc", size: 53, mode: os.FileMode(0420), modTime: time.Unix(1688544460, 0)}
- a := &asset{bytes: bytes, info: info}
+ a := &asset{bytes: []byte(clientIDJSON), info: info}
return a, nil
}
diff --git a/pkg/cli/cmd/auth/utils/const.go b/pkg/cli/cmd/auth/utils/const.go
new file mode 100644
index 00000000000..d8ba175856b
--- /dev/null
+++ b/pkg/cli/cmd/auth/utils/const.go
@@ -0,0 +1,25 @@
+/*
+ Copyright (C) 2022-2023 ApeCloud Co., Ltd
+
+ This file is part of KubeBlocks project
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+
+package utils
+
+const (
+ DefaultBaseURL = "https://apecloud.authing.cn/oidc"
+ OpenAPIHost = "cloudapi.apecloud.cn"
+)
diff --git a/pkg/cli/cmd/auth/utils/utils.go b/pkg/cli/cmd/auth/utils/utils.go
index ec47c5e4752..c57f6dbd468 100644
--- a/pkg/cli/cmd/auth/utils/utils.go
+++ b/pkg/cli/cmd/auth/utils/utils.go
@@ -47,3 +47,21 @@ func NewRequest(ctx context.Context, url string, payload url.Values) (*http.Requ
req.Header.Set("Accept", "application/json")
return req, nil
}
+
+func NewFullRequest(ctx context.Context, url string, method string, header map[string]string, body string) (*http.Request, error) {
+ req, err := http.NewRequestWithContext(
+ ctx,
+ method,
+ url,
+ strings.NewReader(body),
+ )
+ if err != nil {
+ return nil, err
+ }
+ req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+ req.Header.Set("Accept", "application/json")
+ for key, value := range header {
+ req.Header.Set(key, value)
+ }
+ return req, nil
+}
diff --git a/pkg/cli/cmd/cli.go b/pkg/cli/cmd/cli.go
index e0cd1cd68a6..899188337cc 100644
--- a/pkg/cli/cmd/cli.go
+++ b/pkg/cli/cmd/cli.go
@@ -188,8 +188,8 @@ A Command Line Interface for KubeBlocks`,
// Add subcommands
cmd.AddCommand(
- // auth.NewLogin(ioStreams),
- // auth.NewLogout(ioStreams),
+ auth.NewLogin(ioStreams),
+ auth.NewLogout(ioStreams),
// organization.NewOrganizationCmd(ioStreams),
// context.NewContextCmd(ioStreams),
playground.NewPlaygroundCmd(ioStreams),