github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/cli/cmd/auth/authorize/issued_provider.go (about) 1 /* 2 Copyright (C) 2022-2023 ApeCloud Co., Ltd 3 4 This file is part of KubeBlocks project 5 6 This program is free software: you can redistribute it and/or modify 7 it under the terms of the GNU Affero General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU Affero General Public License for more details. 15 16 You should have received a copy of the GNU Affero General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 package authorize 21 22 import ( 23 "context" 24 "fmt" 25 "os" 26 "os/signal" 27 "syscall" 28 "time" 29 30 "github.com/briandowns/spinner" 31 "github.com/hashicorp/go-cleanhttp" 32 "github.com/pkg/errors" 33 "k8s.io/cli-runtime/pkg/genericiooptions" 34 35 "github.com/1aal/kubeblocks/pkg/cli/cmd/auth/authorize/authenticator" 36 "github.com/1aal/kubeblocks/pkg/cli/cmd/auth/utils" 37 "github.com/1aal/kubeblocks/pkg/cli/printer" 38 "github.com/1aal/kubeblocks/pkg/cli/util" 39 ) 40 41 type Options struct { 42 ClientID string `json:"client_id"` 43 AuthURL string 44 NoBrowser bool 45 genericiooptions.IOStreams 46 } 47 48 type CloudIssuedTokenProvider struct { 49 Options 50 Authenticator authenticator.Authenticator 51 } 52 53 func newDefaultIssuedTokenProvider(o Options) (*CloudIssuedTokenProvider, error) { 54 authenticator, err := authenticator.NewAuthenticator(authenticator.PKCE, cleanhttp.DefaultClient(), o.ClientID, o.AuthURL) 55 if err != nil { 56 return nil, errors.Wrap(err, "failed to create authenticator") 57 } 58 return &CloudIssuedTokenProvider{ 59 Options: o, 60 Authenticator: authenticator, 61 }, nil 62 } 63 64 func newIssuedTokenProvider(o Options, authenticator authenticator.Authenticator) (*CloudIssuedTokenProvider, error) { 65 return &CloudIssuedTokenProvider{ 66 Options: o, 67 Authenticator: authenticator, 68 }, nil 69 } 70 71 func (c *CloudIssuedTokenProvider) authenticate(ctx context.Context) (*authenticator.TokenResponse, error) { 72 authorizeResponse, err := c.Authenticator.GetAuthorization(ctx, c.openURLFunc) 73 if err != nil { 74 return nil, err 75 } 76 77 end := c.printProgress("Waiting for confirmation...") 78 defer end() 79 80 tokenResponse, err := c.Authenticator.GetToken(ctx, authorizeResponse) 81 if err != nil { 82 return nil, err 83 } 84 return tokenResponse, nil 85 } 86 87 func (c *CloudIssuedTokenProvider) getUserInfo(token string) (*authenticator.UserInfoResponse, error) { 88 return c.Authenticator.GetUserInfo(context.TODO(), token) 89 } 90 91 func (c *CloudIssuedTokenProvider) refreshToken(refreshToken string) (*authenticator.TokenResponse, error) { 92 tokenResponse, err := c.Authenticator.RefreshToken(context.TODO(), refreshToken) 93 if err != nil { 94 return nil, err 95 } 96 return tokenResponse, nil 97 } 98 99 func (c *CloudIssuedTokenProvider) logout(ctx context.Context, token string) error { 100 end := c.printProgress("Logging out...") 101 defer end() 102 err := c.Authenticator.Logout(ctx, token, c.openURLFunc) 103 if err != nil { 104 return err 105 } 106 return nil 107 } 108 109 func (c *CloudIssuedTokenProvider) printProgress(message string) func() { 110 if !utils.IsTTY() { 111 fmt.Fprintln(c.Out, message) 112 return func() {} 113 } 114 115 s := spinner.New(spinner.CharSets[14], 100*time.Millisecond, spinner.WithWriter(c.Out)) 116 s.Suffix = fmt.Sprintf(" %s", message) 117 118 _ = s.Color("bold", "green") 119 s.Start() 120 121 sig := make(chan os.Signal, 1) 122 signal.Notify(sig, os.Interrupt, syscall.SIGTERM) 123 go func() { 124 <-sig 125 fmt.Fprintf(c.Out, "\033[?25h") 126 os.Exit(0) 127 }() 128 129 return func() { 130 s.Stop() 131 fmt.Fprintf(c.Out, "\033[?25h") 132 } 133 } 134 135 func (c *CloudIssuedTokenProvider) openURLFunc(url string) { 136 if !c.NoBrowser { 137 err := util.OpenBrowser(url) 138 if err != nil { 139 msg := fmt.Sprintf("failed to open a browser: %s", printer.BoldRed(err.Error())) 140 fmt.Fprint(c.Out, msg) 141 } 142 } else { 143 msg := fmt.Sprintf("\nPlease paste this URL into your browser: %s\n\n", printer.BoldGreen(url)) 144 fmt.Fprint(c.Out, msg) 145 } 146 }